我有大量的对象需要保存以供离线使用。 目前,我使用创建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或键值编码进行编码和解码吗? 基本上我希望类中的所有变量都保存到文件中,并在程序启动时返回到对象。 这种方法有效,但创建所有类需要花费时间和精力。
答案 0 :(得分:43)
是的,您可以自动执行此操作。首先将这些导入您的班级:
#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];
}
}
你必须要小心一点。有以下警告:
这适用于数字,bool,对象等属性,但自定义结构不起作用。此外,如果您班级中的任何属性是不支持NSCoding的对象,则不起作用。
这只适用于合成属性,而不适用于ivars。
您可以通过在编码之前检查encodeWithCoder中的值的类型来添加错误处理,或者覆盖setValueForUndefinedKey方法以更优雅地处理问题。
更新:
我已将这些方法包装到库中:https://github.com/nicklockwood/AutoCoding - 库将这些方法实现为NSObject上的类别,因此可以保存或加载任何类,并且还增加了对编译继承属性的支持,我的原始答案没有处理。
更新2:
我已经更新了正确处理继承和只读属性的答案。
答案 1 :(得分:0)
库https://github.com/nicklockwood/AutoCoding的性能不好,因为它每次都使用class_copyPropertyList进行反射,而且它似乎不支持某些结构。
检查https://github.com/flexme/DYCoding,它应该与预编译的代码一样快,并且它支持各种属性类型。