提到归档这块,首先得看了一下,常规的归档方法(又名序列化),把对象转为字节码,以文件的形式存储到磁盘上;程序运行过程中或者当再次重写打开程序的时候,可以通过解归档(反序列化)还原这些对象。
注意NSCoding协议中有两个方法:
-
encodeWithCoder
方法对对象属性进行编码,在对象归档时调用 -
initWithCoder
方法解码归档数据来初始化对象,在对象解归档时调用
#import <Foundation/Foundation.h>
@interface UserModel : NSObject<NSCoding>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end
#import "UserModel.h"
NSString *const kNameKey = @"name";
NSString *const kAgeKey = @"age";
@implementation UserModel
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:self.name forKey:kNameKey];
[aCoder encodeInteger:self.age forKey:kAgeKey];
}
- (id)initWithCoder:(NSCoder *)aDecoder {
if (self == [super init]) {
self.name = [aDecoder decodeObjectForKey:kNameKey];
self.age = [aDecoder decodeIntegerForKey:kAgeKey];
}
return self;
}
@end
//归档(序列化)
// Content
UserModel *model = [[UserModel alloc] init];
model.name = @"Yang";
model.age = 22;
// save
NSString *filePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingString:kYangPath];
NSMutableData *archiverData = [NSMutableData data];
NSKeyedArchiver * archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:archiverData];
[archiver encodeObject:model forKey:kYangKey];
[archiver finishEncoding];
if ([archiverData writeToFile:filePath atomically:YES]) {
NSLog(@"archiver success");
}
//解归档(反序列化)
//get
NSString *filePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingString:kYangPath];
NSData *unarchiverData = [NSData dataWithContentsOfFile:filePath];
NSKeyedUnarchiver *unachiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:unarchiverData];
UserModel *model = [unachiver decodeObjectForKey:kYangKey];
NSLog(@"test name === %@,age === %lu",model.name,model.age);
// output: test name === Yang,age === 22
- 归档和解归档可以用于少量数据的持久化存储和读取
- 属性列表只能存储Foundation框架中的对象,归档除了可以归档Foundation框架中的对象以外,还可以归档实现了NSCoding协议的自定义对象
- 通过归档创建的文件是加密的
然而我们为什么要使用runtime 进行归档呢?
在上面model中,我们的类只有2个属性:name和age,于是我们在存档和解档的时候分别对这两个属性进行了处理,万一这个类的属性很多呢?!一个一个属性写的话,肯定是超级浪费时间的,所以此时runtime 就派上用场啦
此时我们需要用到runtime 的一个优势,获取类中的所有成员属性,此处有两个方法。
OBJC_EXPORT objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
OBJC_EXPORT Ivar *class_copyIvarList(Class cls, unsigned int *outCount)
// 第一个参数:表示获取哪个类中的成员属性
// 第二个参数:表示这个类有多少成员属性,传入一个Int变量地址,会自动给这个变量赋值
注意 : class_copyPropertyList
返回的仅仅是对象类的属性(@property申明的属性),而class_copyIvarList
返回类的所有属性和变量(包括在@interface大括号中声明的变量),此处我们用第一种。(第二种方法也可以尝试)
详细实现
#import "BaseModel.h"
#import <objc/runtime.h>
@implementation BaseModel
// 归档
- (void)encodeWithCoder:(NSCoder *)enCoder{
//归档存储自定义对象
unsigned int count = 0;
//获得指向该类所有属性的指针
objc_property_t *properties = class_copyPropertyList([self class], &count);
for (int i =0; i < count; i ++) {
//获得
objc_property_t property = properties[i];
//根据objc_property_t获得其属性的名称--->C语言的字符串
const char *name = property_getName(property);
NSString *key = [NSString stringWithUTF8String:name];
// 编码每个属性,利用kVC取出每个属性对应的数值
[enCoder encodeObject:[self valueForKeyPath:key] forKey:key];
}
free(properties);
}
// 解档
- (id)initWithCoder:(NSCoder *)aDecoder{
//归档存储自定义对象
unsigned int count = 0;
//获得指向该类所有属性的指针
objc_property_t *properties = class_copyPropertyList([self class], &count);
for (int i =0; i < count; i ++) {
objc_property_t property = properties[i];
//根据objc_property_t获得其属性的名称--->C语言的字符串
const char *name = property_getName(property);
NSString *key = [NSString stringWithUTF8String:name];
//解码每个属性,利用kVC取出每个属性对应的数值
[self setValue:[aDecoder decodeObjectForKey:key] forKeyPath:key];
}
free(properties);
return self;
}
@end
然后其他类使用,只要继承它就好了
#import "BaseModel.h"
@interface PeopleModel : BaseModel
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end
然后.m 中基本不需要做什么,然后就可以正常使用归档或解档啦
提到一点此处用看VC,石油定缺点的,就是效率比正常代码要低一点,但是我也认为这应该是可以接受的。