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

时间:2014-12-06 20:52:07

标签: ios objective-c macos objective-c-runtime

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

如何以不需要在复制方法中对该对象类的属性进行硬编码的方式来执行此操作?

2 个答案:

答案 0 :(得分:6)

无法在Objective-C中将对象转换为子类的实例。但是,使用下面的类,您可以提供对象和子类的实例,并将所有属性的值复制到子类实例。此实现适用于Objective-C对象类型和C基元。您不必指定(或实际上确定)需要复制的属性,只要您知道重要变量是可见的并且可以设置(即,没有属性显示为“只读”或不暴露,其价值不能由班级重新计算)。因此,对于已知类,此方法相对健壮,并且不需要更新以支持您在对象类中进行的适合这些参数的更改。它与iOS 8兼容。

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

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

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

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

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

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

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

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

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

部首:

//  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] != '@') {
            // A C 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

答案 1 :(得分:2)

我需要创建emr create-custer的子类,在--configurations '[{"Classification":"emrfs-site","Properties":{"fs.s3.enableServerSideEncryption":"true"},"Configurations":[]}]' 中使用,并希望保持在Interface Builder中为单元格设置的属性完整。

我使用NSTextFieldCell解决了这个任务,NSTableView用于存储和恢复对象的属性。

由于NSKeyedArchiver实现了initWithCoder,它支持archiver函数,因此我可以使用这段代码从其他属性中启动我的子类:

NSTextFieldCell