(3)进行自定义对象的归档
上面介绍中有提到,原则上,任何遵守了NSCoding协议的类都可以进行归档操作,那么对于我们自定义的对象,我们该如何来做呢?
首先,我们新建一个类:
仿照上面的例子,我们写一个这样的类:
@interface MyObject : NSObject
@property(nonatomic,strong)NSString * name;
@property(nonatomic,assign)int age;
@end
对其进行归档:
//进行归档
MyObject * obj = [[MyObject alloc]init];
obj.name = @"jaki";
obj.age = 24;
NSData * data = [NSKeyedArchiver archivedDataWithRootObject:obj];
//进行解档
MyObject * obj2 = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(@"\nname:%@\nage:%d",obj2.name,obj2.age);
直接运行,程序会崩溃掉,打印如下:
可以看出,正是我们前边说过的,必须遵守归档协议的对象,才可以被归档,我们在MyObject类中实现如下两个方法:
//解档方法
- (instancetype)initWithCoder:(NSCoder *)coder
{
if (self=[super init]) {
_name = [coder decodeObjectForKey:@"name"];
_age = [coder decodeIntForKey:@"age"];
}
return self;
}
//归档方法
- (void)encodeWithCoder:(NSCoder *)coder
{
[coder encodeObject:_name forKey:@"name"];
[coder encodeInt:_age forKey:@"age"];
}
添加了上面两个方法,我们自定义的对象就可以自由归档存取,并可以写入本地,非常cool吧。
三、设计可以归档存取的数据模型基类
1、动机与初衷
通过上面对归档的介绍,我们可以发现归档一个十分有潜力的应用:可以自由存取自定义的数据对象。这个特性的优势是毫无疑问的,除了可以使我们的数据用起来更加方便,无需多次解析数据外,安全性也更好。但是也带来了一个缺陷,每个类都需要实现NSCoding中的两个方法是十分繁琐的,并且类越复杂,这个步骤越繁琐,如果在之后的修改和优化中类做了改变,相应的方法也要做改变,这将增加很大的工作量并且埋下潜在bug的风险。
所以我们会想,能否设计一个这样的model基类,来使需要存储的model都继承于它,使我们的model不需要实现NSCoding方法的同时可以支持归档呢,通过runtime和OC语言特性的一些小技巧,我们是可以做到的。
2、基类模型的设计
我们新建一个BaseModel类,核心方法如下:
//归档与解归档的方法
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super init];
if (self) {
//获取所有属性
NSArray * porpertyArray = [self getAllPropertys];
for (NSString * name in porpertyArray) {
//去掉属性名前面的_
NSString * key = [name substringFromIndex:1];
//约定好的键值对 c+key
[self setValue:[coder decodeObjectForKey:[NSString stringWithFormat:@"c%@",key]] forKey:key];
}
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder
{
//获取所有属性
NSArray * porpertyArray = [self getAllPropertys];
for (NSString * name in porpertyArray) {
//去掉属性名前面的_
NSString * key = [name substringFromIndex:1];
//约定好的键值对 c+key
[coder encodeObject:[self valueForKey:key] forKey:[NSString stringWithFormat:@"c%@",key]];
}
}
//获取model所有属性
-(NSArray *)getAllPropertys{
NSMutableArray * array = [[NSMutableArray alloc]init];
unsigned int * count = malloc(sizeof(unsigned int));
//调用runtime的方法
//Ivar:方法返回的对象内容对象,这里将返回一个Ivar类型的指针
//class_copyIvarList方法可以捕获到类的所有变量,将变量的数量存在一个unsigned int的指针中
Ivar * mem = class_copyIvarList([self class], count);
//进行遍历
for (int i=0; i< *count ; i++) {
//通过移动指针进行遍历
Ivar var = * (mem+i);
//获取变量的名称
const char * name = ivar_getName(var);
NSString * str = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
[array addObject:str];
}
//释放内存
free(count);
//注意处理野指针
count=nil;
return array;
}
通过这样的一个runtime机制,我们可以很方便的是新建的model继承于这个基类,无需其他处理直接支持归档,修改与优化都不受影响。
四、为志同道合的朋友分享
这个model集成在了我的一个开源的开发框架中,当然,那里面也综合和许多许多这样方便开发者使用的功能,如果你感兴趣,可以在https://github.com/ZYHshao/YHBaseFoundationTest上面看到。如果你发现了一些bug或者可以添加或者优化的地方,请务必告知我,十分你感谢。QQ:316045346