Runtime - 多属性快速归档解档
对象归档,就是把内存中对象存储到本地。(归档的实质就是将OC对象拆分为键值对的字典,然后变成二进制存入磁盘)
对象解档,就是把本地的对象读取到内存。
常规的归解档:
创建一个Model类:Student(遵循NSCoding协议,实现归解档协议方法)
// Student.h
#import <Foundation/Foundation.h>
@interface Student : NSObject<NSCoding>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSUInteger age;
@property (nonatomic, copy) NSString *sex;
@end
// Student.m
#import "Student.h"
@implementation Student
//告诉系统归档哪些属性
- (void)encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject:_name forKey:@"name"];
[aCoder encodeInteger:_age forKey:@"age"];
[aCoder encodeObject:_sex forKey:@"sex"];
}
//解档的属性
-(instancetype)initWithCoder:(NSCoder *)aDecoder{
if (self = [super init]) {
_name = [aDecoder decodeObjectForKey:@"name"];
_age = [aDecoder decodeIntegerForKey:@"age"];
_sex = [aDecoder decodeObjectForKey:@"sex"];
}
return self;
}
在需要归解档的地方写上归解档方法并调用
//归档方法
- (void)save{
//创建一个用于归档的对象并赋值
Student *s = [[Student alloc]init];
s.name = @"haha";
s.age = 18;
s.sex = @"boy";
//获取沙沙盒路径
NSString *temPath = NSTemporaryDirectory();
//创建文件路径 stringByAppendingPathComponent 不用再加“/”
NSString *filePath = [temPath stringByAppendingPathComponent:@"student.wsy"];
NSLog(@"filePath==%@",filePath);
//归档 file:绝对路径 需要遵守NSCoding协议
[NSKeyedArchiver archiveRootObject:s toFile:filePath];
}
//解档方法
- (void)read{
//获取沙沙盒路径
NSString *temPath = NSTemporaryDirectory();
//获取要解档文件路径
NSString *filePath = [temPath stringByAppendingPathComponent:@"student.wsy"];
Student *s = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
NSLog(@"名字:%@ 年龄:%zi 性别:%@",s.name, s.age, s.sex);
}
调用归档方法结果:(创建了文件,对象应该也存进去了,一会解档验证)
调用解档方法结果:(取出了对象,证明归解档成功)
那么我们就可以思考一下:如果对象属性特别多的时候,在协议方法里实现属性encode和decode就会比较麻烦,需要写好多encode 和 decode
而如果利用循环的话,我们需要知道三点:属性个数、各属性名称、各具体属性
//告诉系统归档哪些属性(解档同理)
- (void)encodeWithCoder:(NSCoder *)aCoder{
// 伪代码 1.遍历属性个数 2.获取属性名称
for (int i = 0; i < 属性个数 ; i++) {
[[aCoder encodeObject:属性 forKey:属性名称];]
}
}
下面就可以用到Runtime了,使用Runtime需要导入#import <objc/runtime.h>
我们可以利用 Ivar - 成员属性
//方法:class_copyIvarList(<#__unsafe_unretained Class cls#>, <#unsigned int *outCount#>)
//两个参数:
//__unsafe_unretained Class cls class类型
//unsigned int *outCount 无符号自增整型指针
//返回值为Ivar * 类型 是个指向ivar列表地址的指针 (可以通过KVC进行修改了,小心私有API)
//示例
unsigned int count = 0;
Ivar * ivars = class_copyIvarList(NSClassFromString(@"Student"), &count);
//count实质就是属性的个数(甚至放在.m里的私有属性一样能够统计进去)
NSLog(@"count==%u",count);
//ivars 是属性列表指针 通过++可以往下逐一获取到ivar,当然注意边界,越界不会崩溃,越界之后如果拿到垃圾信息就会报错
Ivar ivar = ivars[0];
//通过ivar_getName 可以获取到名字(返回值为c语言字符串)
const char *name = ivar_getName(ivar);
NSLog(@"%s",name);
打印结果:(我的Student里确实有三个属性,且.m里米有私有属性 第0个位置的确实是name属性)
OK,那么我们就可以愉快的使用了,所以model类的.m文件就变成了这样:
// Student.m
#import "Student.h"
#import <objc/runtime.h>
@implementation Student
//告诉系统归档哪些属性
- (void)encodeWithCoder:(NSCoder *)aCoder{
//属性个数
unsigned int count = 0;
Ivar * ivars = class_copyIvarList([Student class], &count);
for (int i = 0; i < count; i++) {
//拿到属性
Ivar ivar = ivars[i];
//拿到名称
const char *name = ivar_getName(ivar);
//转换成OC字符串
NSString *key = [NSString stringWithUTF8String:name];
[aCoder encodeObject:[self valueForKey:key] forKey:key];
}
//C语言里由Creat Copy创造的指针需要手动释放 ARC只管OC对象
free(ivars);
}
//解档的属性
-(instancetype)initWithCoder:(NSCoder *)aDecoder{
if (self = [super init]) {
//属性个数
unsigned int count = 0;
Ivar * ivars = class_copyIvarList([Student class], &count);
for (int i = 0; i < count; i++) {
//拿到属性
Ivar ivar = ivars[i];
//拿到名称
const char *name = ivar_getName(ivar);
//转换成OC字符串
NSString *key = [NSString stringWithUTF8String:name];
//解档
id value = [aDecoder decodeObjectForKey:key];
//设置到属性上
[self setValue:value forKey:key];
}
//C语言里由Creat Copy创造的指针需要手动释放 ARC只管OC对象
free(ivars);
}
return self;
}
OK,这里写完就可以愉快的进行归解档了,属性再多也不怕了!