Objective c 实现启用NSCoder类的简单方法

Objective c 实现启用NSCoder类的简单方法,objective-c,ios,key-value,nscoder,Objective C,Ios,Key Value,Nscoder,我有很多要保存以供脱机使用的对象。 目前,我使用createnscoder兼容类来创建对象,并将编码后的数据保存到文件中,以便脱机使用 因此,在.h中,我介绍了对象: @interface MyClass : NSObject<NSCoding>{ NSNumber* myObject;} @property(nonatomic,retain) NSNumber* myObject; 因此,该类只是带有getter和setter的虚拟存储。 这里有更好的解码/编码方法吗。 我可以使

我有很多要保存以供脱机使用的对象。 目前,我使用createnscoder兼容类来创建对象,并将编码后的数据保存到文件中,以便脱机使用

因此,在.h中,我介绍了对象:

@interface MyClass : NSObject<NSCoding>{
NSNumber* myObject;}
@property(nonatomic,retain) NSNumber* myObject;
因此,该类只是带有getter和setter的虚拟存储。 这里有更好的解码/编码方法吗。 我可以使用@dynamic或键值编码进行编码和解码吗? 基本上,我希望类中的所有变量都保存到文件中,并在程序启动时返回到对象。
这种方法可行,但创建所有类都需要时间和精力。

是的,您可以自动完成。首先将这些导入到您的类中:

#import <objc/runtime.h> 
#import <objc/message.h>
你得小心一点。有以下注意事项:

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

  • 这只适用于合成属性,而不适用于IVAR

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

    更新:

    我已经将这些方法包装到一个库中:-库将这些方法作为NSObject上的一个类别来实现,因此可以保存或加载任何类,它还添加了对继承属性编码的支持,这是我最初的答案无法处理的

    更新2:

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

    该库的性能不好,因为它每次都使用class_copyPropertyList进行反射,而且似乎不支持某些结构


    检查,它应该与预编译代码一样快,并且支持各种属性类型。

    感谢您的回答,这非常适合我的目的。我会投你一票,但我没有足够的声誉..代码有小内存泄漏。您需要添加:免费(属性);对propertyKeys的方法。很好的回答。我无意中在这里遇到了问题,但我在阅读时学到了很多。我发现的一个警告是,这种方法不能从基类中解构继承的属性。i、 在基类中有一个属性“uuid”,它不能在派生类中正确处理。是的。请查看我的自动编码库中更高级的实现,它可以正确处理继承的属性:
    #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;
    }
    
    - (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];
        }
    }