Runtime 有什么用?
- 利用Runtime运行时,在程序的运行过程中,动态创建一个类
- 利用Runtime运行时,在程序运行中动态修改一个类的属性/方法
- 利用Runtime运行时,遍历一个类的所有属性和方法
这里来介绍一下怎么利用在Runtime运行时,通过遍历一个类的所有属性来实现序列化和反序列化
注:这里讲的是复杂对象的归档,基本数据类型的归档这里暂不做介绍
相比常规写法的优点:
- 快捷,直接在BaseModel中添加下面编码反编码这两段代码即可
- 省心,不用关心每个模型中属性的类型
- 安全,手动逐个属性进行编码反编码容易出错
代码示例:对Person的属性进行编码和反编码(先遵循NSCoding协议)
1.编码
- (void)encodeWithCoder:(NSCoder *)aCoder {
// 常规写法
/*
[aCoder encodeObject:_name forKey:@"name"];
[aCoder encodeObject:_gender forKey:@"gender"];
[aCoder encodeInteger:_age forKey:@"age"];
*/
// Runtime写法
// 1.导入头文件 <objc/message.h>
// 2.获取这个类的所有成员变量列表的指针列表(这个可以在程序的任何地方获取,获取的成员变量包括这个类的私有属性)
unsigned int count = 0;
// c语言里面,如果传递一个基本数据类型的指针,那一般都是需要在函数内部修改它的值!
Ivar *ivars = class_copyIvarList([self class], &count);
for (int i = 0; i < count; i++) {
// 拿出属性的名称
Ivar ivar = ivars[i];
const char *name = ivar_getName(ivar);
// C语言字符串转化为OC字符串
NSString *key = [NSString stringWithUTF8String:name];
// 通过KVC取出属性对应的值
id value = [self valueForKey:key];
// 编码
[aCoder encodeObject:value forKey:key];
}
// 注意:使用C语言需要释放指针
free(ivars);
}
2.反编码
// 需要先服从NSCoding协议
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self) {
// 常规写法
/*
_name = [aDecoder decodeObjectForKey:@"name"];
_gender = [aDecoder decodeObjectForKey:@"gender"];
_age = [aDecoder decodeIntegerForKey:@"age"];
*/
// runtime写法
unsigned int count = 0;
Ivar *ivars = class_copyIvarList([self class], &count);
for (int i = 0; i < count; i++) {
// 获取属性的名称
Ivar ivar = ivars[i];
const char *name = ivar_getName(ivar);
// C语言字符串转化为OC字符串
NSString *key = [NSString stringWithUTF8String:name];
// 反编码
id value = [aDecoder decodeObjectForKey:key];
// 通过KVC将值设置到对应的属性上
[self setValue:value forKey:key];
}
// 注意:使用C语言需要释放指针
free(ivars);
}
return self;
}
归档和反归档
1.归档(序列化)
// 归档路径
- (NSString *)getFilePath{
// 注:这里存储的是二进制类型的数据,故文件后缀名随便写
return [NSTemporaryDirectory() stringByAppendingPathComponent:self.fileName];
}
// 归档
- (IBAction)archiveAction:(id)sender {
Person *per = [[Person alloc] init];
per.name = @"Jack";
per.age = 20;
per.gender = @"man";
// 设置文件名 注:这里存储的是二进制类型的数据,故文件后缀名随便写
self.fileName = [NSString stringWithFormat:@"%@%@", per.name, @".per"];
// 归档
[NSKeyedArchiver archiveRootObject:per toFile:[self getFilePath]];
// 检测是否归档成功
BOOL result = [[NSFileManager defaultManager] fileExistsAtPath:[self getFilePath]];
if (result) {
[SYProgressHUD showSuccessText:@"序列化成功"];
} else {
[SYProgressHUD showFailureText:@"序列化失败"];
}
}
2.反归档(反序列化)
- (IBAction)unarchiveAction:(id)sender {
// 解档
Person *per = [NSKeyedUnarchiver unarchiveObjectWithFile:[self getFilePath]];
// 显示读取的文件
_showReadInfoLB.text = [NSString stringWithFormat:@"name: %@ gender: %@ age: %ld", per.name, per.gender, per.age];
NSLog(@"%@", _showReadInfoLB.text);
}