轻松实现NSCoder启用课程 [英] Easy way to do NSCoder enabled class

查看:116
本文介绍了轻松实现NSCoder启用课程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有大量的对象需要保存以供离线使用。
目前我使用创建NSCoder兼容类的对象和编码数据到文件离线可用。

I have tons of objects which I want to save for offline use. Currently I use create NSCoder compliant classes for the objects and coded data to file to be available offline.

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

So in the .h I introduce the objects:

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

并且在.m中我做的是:

And in .m I make the inits:

- (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或键值编码进行编码和解码吗?
基本上我希望类中的所有变量都保存到文件中,并在程序启动时返回到对象。
此方法有效,但创建所有类需要花费时间和精力。

So the class is just dummy storage with getter and setter. Is here any better way to do the decode / encode. Can I use somehow @dynamic or Key-value coding for encode and decode? Basically I want all the variables in class saved to file and back to object when program starts up. This approach work, but creating all classes takes time and effort.

推荐答案

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

Yes, you can do this automatically. First import these into your class:

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

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

Now add this method, which will use low-level methods to get the property names:

- (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方法:

Then here are your NSCoder methods:

- (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];
    }
}

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

You have to be a bit careful with this. There are the following caveats:


  1. 这适用于数字,bool,对象等属性,但自定义结构赢了不行。此外,如果您的类中的任何属性是不支持NSCoding的对象,则不起作用。

  1. This will work for properties that are numbers, bools, objects, etc, but custom structs won't work. Also, if any of the properties in your class are objects that don't themeselves support NSCoding, this won't work.

这仅适用于合成属性,而不是ivars。

This will only work with synthesized properties, not ivars.

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

You could add error handling by checking the type of a value in encodeWithCoder before encoding it, or overriding the setValueForUndefinedKey method to handle a problem more gracefully.

更新:

我已经包装了这些方法进入库: https://github.com/nicklockwood/AutoCoding - 库将这些方法作为NSObject上的类别实现,所以任何类都可以保存或加载,它还增加了对继承属性编码的支持,我的原始答案无法处理。

I've wrapped these methods up into a library: https://github.com/nicklockwood/AutoCoding - the library implements these methods as a category on NSObject so any class can be saved or loaded, and it also adds support for coding inherited properties, which my original answer doesn't handle.

更新2:

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

I've updated the answer to correctly deal with inherited and read-only properties.

这篇关于轻松实现NSCoder启用课程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆