在Objective-C中将对象转换(或复制)到子类实例

我想将一个对象的实例转换为该对象类的子类的一个实例,以便我可以在Objective-C中使用该子类的其他方法和属性。

我怎样才能做到这一点,而不需要我在复制方法中硬编码该对象类的属性?

在Objective-C中将对象转换为子类的实例是不可能的。 但是,下面的类可以提供对象和子类的实例,并将所有属性的值复制到子类实例中。 这个实现与Objective-C对象types和C原语一起工作。 您不必指定(甚至确定)需要复制的属性,只要知道重要的variables是可见的并且可以设置即可(即,没有属性暴露为“只读”或不根本就没有暴露,其价值不能由class级重新计算)。 这个方法对于已知的类是相对健壮的,并且不需要更新来支持你在对象类中做出的适合这些参数的未来改变。 它与iOS 8兼容。

这个类提供了四个类的方法:

+ (id) copyObject:(id)object toSubclassObject:(id)subclassObject 

将对象的所有属性复制到subclassObject。 如果subclassObject不是对象的子类,则返回nil。

 + (NSDictionary *) propertiesOfObject:(id)object; 

返回对象的所有可见属性的字典,包括来自其所有超类(NSObject除外)的字典。

 + (NSDictionary *) propertiesOfClass:(Class)class; 

返回类的所有可见属性的字典,包括来自其所有超类(NSObject除外)的字典。

 + (NSDictionary *) propertiesOfSubclass:(Class)class; 

返回特定于子类的所有可见属性的字典。 包括其超类的属性。

标题:

 // SYNUtilities.h #import <Foundation/Foundation.h> @interface SYNUtilities : NSObject + (id) copyObject:(id)object toSubclassObject:(id)subclassObject; + (NSDictionary *) propertiesOfObject:(id)object; + (NSDictionary *) propertiesOfClass:(Class)class; + (NSDictionary *) propertiesOfSubclass:(Class)class; @end 

执行:

 #import "SYNUtilities.h" #import <objc/runtime.h> #import <objc/message.h> @implementation SYNUtilities + (id) copyObject:(id)object toSubclassObject:(id)subclassObject { if (![[subclassObject class] isSubclassOfClass:[object class]]) { return nil; } NSDictionary * properties = [self propertiesOfObject:object]; NSLog(@"Properties of %@:\n%@", [object class], properties); for (NSString * property in properties) { SEL selector = NSSelectorFromString(property); if (selector) { id value = [object valueForKey:property]; [subclassObject setValue:value forKey:property]; } } return subclassObject; } + (NSDictionary *) propertiesOfObject:(id)object { Class class = [object class]; return [self propertiesOfClass:class]; } + (NSDictionary *) propertiesOfClass:(Class)class { if (class == NULL) { return nil; } NSMutableDictionary * properties = [NSMutableDictionary dictionary]; [self propertiesForHierarchyOfClass:class onDictionary:properties]; return [NSDictionary dictionaryWithDictionary:properties]; } + (NSDictionary *) propertiesOfSubclass:(Class)class { if (class == NULL) { return nil; } NSMutableDictionary *properties = [NSMutableDictionary dictionary]; return [self propertiesForSubclass:class onDictionary:properties]; } + (NSMutableDictionary *)propertiesForHierarchyOfClass:(Class)class onDictionary:(NSMutableDictionary *)properties { if (class == NULL) { return nil; } if (class == [NSObject class]) { // On reaching the NSObject base class, return all properties collected. return properties; } // Collect properties from the current class. [self propertiesForSubclass:class onDictionary:properties]; // Collect properties from the superclass. return [self propertiesForHierarchyOfClass:[class superclass] onDictionary:properties]; } + (NSMutableDictionary *) propertiesForSubclass:(Class)class onDictionary:(NSMutableDictionary *)properties { unsigned int outCount, i; objc_property_t *objcProperties = class_copyPropertyList(class, &outCount); for (i = 0; i < outCount; i++) { objc_property_t property = objcProperties[i]; const char *propName = property_getName(property); if(propName) { const char *propType = getPropertyType(property); NSString *propertyName = [NSString stringWithUTF8String:propName]; NSString *propertyType = [NSString stringWithUTF8String:propType]; [properties setObject:propertyType forKey:propertyName]; } } free(objcProperties); return properties; } static const char *getPropertyType(objc_property_t property) { const char *attributes = property_getAttributes(property); char buffer[1 + strlen(attributes)]; strcpy(buffer, attributes); char *state = buffer, *attribute; while ((attribute = strsep(&state, ",")) != NULL) { if (attribute[0] == 'T' && attribute[1] != '@') { // AC primitive type: /* For example, int "i", long "l", unsigned "I", struct. Apple docs list plenty of examples of values returned. For a list of what will be returned for these primitives, search online for "Objective-c" "Property Attribute Description Examples" */ NSString *name = [[NSString alloc] initWithBytes:attribute + 1 length:strlen(attribute) - 1 encoding:NSASCIIStringEncoding]; return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding]; } else if (attribute[0] == 'T' && attribute[1] == '@' && strlen(attribute) == 2) { // An Objective C id type: return "id"; } else if (attribute[0] == 'T' && attribute[1] == '@') { // Another Objective C id type: NSString *name = [[NSString alloc] initWithBytes:attribute + 3 length:strlen(attribute) - 4 encoding:NSASCIIStringEncoding]; return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding]; } } return ""; } @end 

我需要创build一个NSTextFieldCell的子类,在NSTableView ,并希望保持在Interface Builder中为单元格设置的属性。

我通过使用NSKeyedArchiver解决了这个任务,这是为了存储和恢复一个对象的属性。

由于NSTextFieldCell实现了initWithCoder,因此它支持归档函数,因此我可以使用这段代码从另一个属性启动我的子类:

 - (id)initWithCell:(NSCell *)cell { // Use NSArchiver to copy the NSCell's properties into our subclass NSMutableData *data = [NSMutableData data]; NSKeyedArchiver *arch = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; [cell encodeWithCoder:arch]; [arch finishEncoding]; NSKeyedUnarchiver *ua = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; self = [self initWithCoder:ua]; // Here I'd set up additional properties of my own class return self; }