【问题标题】:Easy way to do NSCoder enabled class做 NSCoder 启用类的简单方法
【发布时间】:2012-01-20 07:21:09
【问题描述】:

我有大量的对象要保存以供离线使用。 目前,我使用创建符合 NSCoder 的类,以便将对象和编码数据归档以供离线使用。

所以我在 .h 中介绍了对象:

@interface MyClass : NSObject<NSCoding>{
NSNumber* myObject;}
@property(nonatomic,retain) NSNumber* myObject;

我在 .m 中进行初始化:

- (id) initWithCoder: (NSCoder *)coder {
   if (self = [super init]) {
        [self setMyObject: [coder decodeObjectForKey:@"myObject"]];
   }
}

- (void) encodeWithCoder: (NSCoder *)coder { 
    [coder encodeObject: myObject forKey:@"myObject"];
}

所以这个类只是带有 getter 和 setter 的虚拟存储。 这里有更好的解码/编码方法吗? 我可以以某种方式使用@dynamic 或键值编码进行编码和解码吗? 基本上我希望在程序启动时将类中的所有变量保存到文件并返回到对象。 这种方法可行,但创建所有类需要时间和精力。

【问题讨论】:

    标签: objective-c ios key-value nscoder


    【解决方案1】:

    是的,您可以自动执行此操作。首先将这些导入您的课程:

    #import <objc/runtime.h> 
    #import <objc/message.h>
    

    现在添加这个方法,它将使用低级方法来获取属性名称:

    - (NSArray *)propertyKeys
    {
        NSMutableArray *array = [NSMutableArray array];
        Class class = [self class];
        while (class != [NSObject class])
        {
            unsigned int propertyCount;
            objc_property_t *properties = class_copyPropertyList(class, &propertyCount);
            for (int i = 0; i < propertyCount; i++)
            {
                //get property
                objc_property_t property = properties[i];
                const char *propertyName = property_getName(property);
                NSString *key = [NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding];
    
                //check if read-only
                BOOL readonly = NO;
                const char *attributes = property_getAttributes(property);
                NSString *encoding = [NSString stringWithCString:attributes encoding:NSUTF8StringEncoding];
                if ([[encoding componentsSeparatedByString:@","] containsObject:@"R"])
                {
                    readonly = YES;
    
                    //see if there is a backing ivar with a KVC-compliant name
                    NSRange iVarRange = [encoding rangeOfString:@",V"];
                    if (iVarRange.location != NSNotFound)
                    {
                        NSString *iVarName = [encoding substringFromIndex:iVarRange.location + 2];
                        if ([iVarName isEqualToString:key] ||
                            [iVarName isEqualToString:[@"_" stringByAppendingString:key]])
                        {
                            //setValue:forKey: will still work
                            readonly = NO;
                        }
                    }
                }
    
                if (!readonly)
                {
                    //exclude read-only properties
                    [array addObject:key];
                }
            }
            free(properties);
            class = [class superclass];
        }
        return array;
    }
    

    那么这里是你的 NSCoder 方法:

    - (id)initWithCoder:(NSCoder *)aDecoder
    {
        if ((self = [self init]))
        {
            for (NSString *key in [self propertyKeys])
            {
                id value = [aDecoder decodeObjectForKey:key];
                [self setValue:value forKey:key];
            }
        }
        return self;
    }
    
    - (void)encodeWithCoder:(NSCoder *)aCoder
    {
        for (NSString *key in [self propertyKeys])
        {
            id value = [self valueForKey:key];
            [aCoder encodeObject:value forKey:key];
        }
    }
    

    你必须小心一点。有以下注意事项:

    1. 这适用于数字、布尔值、对象等属性,但自定义结构不起作用。此外,如果您的类中的任何属性是本身不支持 NSCoding 的对象,这将不起作用。

    2. 这仅适用于合成属性,不适用于 ivars。

    您可以通过在编码之前检查 encodeWithCoder 中的值的类型来添加错误处理,或者重写 setValueForUndefinedKey 方法以更优雅地处理问题。

    更新:

    我已将这些方法封装到一个库中:https://github.com/nicklockwood/AutoCoding - 该库将这些方法实现为 NSObject 上的一个类别,因此可以保存或加载任何类,它还添加了对编码继承属性的支持,这是我原来的答案无法处理。

    更新 2:

    我已经更新了正确处理继承和只读属性的答案。

    【讨论】:

    • 感谢您的出色回答,这非常适合我的目的。我会给你一票,但我没有足够的声誉..
    • 代码有少量内存泄漏。你需要添加:free(properties);到 propertyKeys 方法。
    • 优秀的答案。只是偶然在这里偶然发现,但我只是阅读它就学到了很多东西。
    • 我发现的一个警告是,这种方法无法从基类中解压缩继承的属性。即基类中有属性“uuid”,在派生类中无法正确处理。
    • 是的,这是真的。在我的 AutoCoding 库中查看更高级的实现,它可以正确处理继承的属性:github.com/nicklockwood/AutoCoding
    【解决方案2】:

    https://github.com/nicklockwood/AutoCoding 的性能不好,因为它每次都使用 class_copyPropertyList 进行反射,而且它似乎不支持某些结构。

    查看https://github.com/flexme/DYCoding,应该和预编译代码一样快,并且支持各种属性类型。

    【讨论】:

      猜你喜欢
      • 2015-07-03
      • 2010-10-06
      • 1970-01-01
      • 1970-01-01
      • 2013-10-11
      • 1970-01-01
      • 2014-03-23
      • 2016-07-31
      • 2014-06-19
      相关资源
      最近更新 更多