【问题标题】:Transform (or copy) an object to a subclass instance in Objective-C将对象转换(或复制)到 Objective-C 中的子类实例
【发布时间】:2015-02-04 19:58:20
【问题描述】:

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

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

【问题讨论】:

  • 一般来说,不,没有办法做到这一点。您在下面提出的方案适用于 一些 对象,但远非万无一失,如果您已仔细验证源类和目标类“公开”所有重要设置,则仅应在“生产”代码中使用作为可设置的属性。

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


【解决方案1】:

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

该类提供了四种类方法:

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

将对象的所有属性复制到子类对象。如果 subclassObject 不是 object 的子类,则返回 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] != '@') {
            // 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

【讨论】:

    【解决方案2】:

    我需要创建一个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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-11-15
      • 1970-01-01
      • 2011-10-07
      • 1970-01-01
      • 2012-07-29
      • 2010-10-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多