在昨天看了一个视频,讲解的是归档和解档,也就是“序列化”和“反序列化”。觉得自己所学知识真是太少了!!底层的东西懂得几乎为zero。看了就要记下来,就要去理解,去消化,这是学习,成长的必经之路。虽然辛苦,但是值得。因为,在这其中你可以有很多的乐趣,可以沉浸在里面,感觉很舒服。。。
言归正传:你知道数据持久化的方式有哪几种吗?你猜。。
什么是Serialization (序列化)?
写数据到本地磁盘或者进行传输时,需要进行序列化,转化成二进制流,从而便于传输和存储。同理,得到二进制流后,需要进行反序列化,还原成可以使用的数据。
需要注意的是,不同的环境,serialize和unserialize是不同的。一般同一环境中的操作才能得到正确的数据。
接下来是进行归档的代码:
友情提示:请同学们导入头文件 <objc/message.h>!!!
归档的代码:
//归档
[NSKeyedArchiver archiveRootObject:@"归档的内容" toFile:filePath];
/*归档的内容可以是任意格式和内容*/
还需要给归档的内容一个存储的路径,另外还可以添加一个后缀名,比如:.txt、.exe、.han、.我的、.dog、.person。。。。就是说,后缀随便自己设置即可(是不是挺好滴呢,想说没说的都可以加上,哼哼)
创建文件并归档:外部设置
Dog *dog = [[Dog alloc] init];
dog.color = @"黑色";
dog.age = 3;
//文件路径:保存一个临时文件
NSString *tempPath = NSTemporaryDirectory();
//后缀名给了一个.dog
NSString *filePath = [tempPath stringByAppendingPathComponent:@"myDog.dog"];
//归档
[NSKeyedArchiver archiveRootObject:dog toFile:filePath];
到这里,有同学就问了:NSTemporaryDirectory()这个方法干甚用滴?创建的是临时文件!!!
获取路径的代码可以直接在viewDIdLoad里面就可以了,代码:
//打印输出临时文件路径
NSLog(@"%@", NSTemporaryDirectory());
给同学们看一下返回的路径(模拟器上):
那么 stringByAppendingPathComponent 为什么要选择这个呢?当然是为了偷懒不用添加"/"了,哼哼。。
现在你就可以去访问文件了 command+shift+G ,粘贴路径,回车!!!别跟我说你这都不会!你创建的这个文件,什么工具都打不开,只有你自己可以打开:进行 解档(反序列化)。
我创建了一个Dog类,并且赋予它两个属性:
//年龄
@property (nonatomic, assign) NSInteger age;
//颜色
@property (nonatomic, copy) NSString *color;
在外部调用的调用的时候就比较简单了:
ViewController.m中代码:归档(序列化)的已经在上面写过了,接下来是解档(反序列化):外部调用
//文件路径:保存一个临时文件,解档时候当然要获取文件啦
NSString *tempPath = NSTemporaryDirectory();
NSString *filePath = [tempPath stringByAppendingPathComponent:@"myDog.dog"];
//解档
Dog *dog = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
NSLog(@"我的小狗今年%ld岁,它毛是%@的", (long)dog.age, dog.color);
输出的结果(有图有真相):
那么问题来了:在Dog类里面是怎么写的呢?问得好!请同学们看下面的精彩之处!!
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeInteger:_age forKey:@"age"];
[aCoder encodeObject:_color forKey:@"color"];
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super init]) {
_color = [aDecoder decodeObjectForKey:@"color"];
_age = [aDecoder decodeIntegerForKey:@"age"];
}
return self;
}
是否有同学又要问了:如果这只小狗比较牛B,有10个、20个属性呢?难道要这样写20几个归档解档代码吗?太low了吧?问得好!那么怎么解决该问题呢?用 RunTime!!!对!你没听错,是他,是他,就是他!!
归档:Dog.m
- (void)encodeWithCoder:(NSCoder *)aCoder {
//count是什么意思呢?
unsigned int count = 0;
Ivar *ivars = class_copyIvarList([Dog class], &count);
for (int i = 0; i < count; i ++) {
//所有的成员属性
Ivar ivar = ivars[i];
//那都属性的名称
const char *name = ivar_getName(ivar);
//设置key
NSString *key = [NSString stringWithUTF8String:name];
//通过KVC归档
[aCoder encodeObject:[self valueForKey:key] forKey:key];
}
//class_copyIvarList是c语言,需要手动管理内存。
free(ivars);
}
下面对代码解析一下:
(1)class_copyIvarList(<#__unsafe_unretained Class cls#> , <#unsigned int ***outCount#> )
获取某类的所有属性列表,包括@property和 { } 中设置的属性。顺便说一下: class_copyPropertyList 则是只有 @property 声明的属性。
点进去之后的方法显示:里面的参数type需要在外面进行设置。
(2)OBJC_EXPORT Ivar ***class_copyIvarList(Class cls, unsigned int *outCount)
返回的类型是:Ivar*,即用“Ivar *”接受数组(属性列表)。
看一下count的类型:unsigned int * outCount,获取到的是属性的个数。unsigned int count = 0;
//下面的打印输出属性个数:
class_copyIvarList(NSClassFromString(@"Dog"), &count);
NSLog(@"Dog属性个数:%d", count);
(3) Ivar ivar = ivars[i];遍历数组中的ivar,为何没有 * 了呢?附加一篇:Ivar
**typedef struct objc_ivar *Ivar; **这是一个结构体!!!
(4)const char *name = ivar_getName(ivar);
请看过OBJC_EXPORT const char *ivar_getName(Ivar v),里面的参数就是没有 * 的哦。获取的是每一个属性。
我们都明白的一个事情是:归档之后就是UTF8格式的啦,获取属性就相当于其中的key。
(4)我们找到了key,那么value不就好弄了吗,通过KVC,欧耶,归档成功!!!!
注意:一定要释放!!因为他是C,手动管理是必须的。 free(ivars);
解档:Dog.m
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super init]) {
//count是什么意思呢?
unsigned int count = 0;
Ivar *ivars = class_copyIvarList([Dog class], &count);
for (int i = 0; i < count; i ++) {
//所有的成员属性
Ivar ivar = ivars[i];
//那都属性的名称
const char *name = ivar_getName(ivar);
//设置key
NSString *key = [NSString stringWithUTF8String:name];
//利用KVC解档
id value = [aDecoder decodeObjectForKey:key];
[self setValue:value forKey:key];
}
//class_copyIvarList是c语言,需要手动管理内存。
free(ivars);
}
return self;
}
解档没有什么可说的,跟上面归档的节奏基本是一样的,就是记得解档之后的类型是任意的 id 类型,然后利用KVC进行赋值。[self setValue:value forKey:key];即可将其反序列化。
我们使用这些东西可以干什么呢?答:建模型!没错,创建Model。
Runtime的特点是什么?附上一个链接:RunTime
(1)动态创建类。
(2)动态添加属性、方法。
(3)消息机制。
Dog *dog = [[Dog alloc] init];
//一个food方法
[dog foods];
[dog performSelector:@selector(speed) withObject:nil];
//方法名
NSLog(@"%s", __func__);
//消息传递机制
objc_msgSend(dog, @selector(speed));
//消息传递机制
objc_msgSend(dog, sel_registerName("foods"));
以上是简单的对RunTime的使用和理解。以上的链接可以有所帮助。