对遵循了NSCoding
协议的iOS对象进行归档和解档是我们经常用到的一种数据持久化方式。
- 创建一个
People
类,并遵循NSCoding
协议:
@interface People : NSObject<NSCoding>
//需要归档的属性
@property(nonatomic,copy)NSString * time;
@property(nonatomic,copy)NSArray * dataArray;
@end
- 在
People.m
中实现NSCoding
的协议方法:
#pragma mark - 实现NSCoding的协议方法
- (void)encodeWithCoder:(NSCoder *)aCoder {
//需要归档的属性
[aCoder encodeObject:self.time forKey:@"time"];
[aCoder encodeObject:self.dataArray forKey:@"dataArray"];
NSLog(@"调用了 encodeWithCoder:");
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self) {
//解档
self.time = [aDecoder decodeObjectForKey:@"time"];
self.dataArray = [aDecoder decodeObjectForKey:@"dataArray"];
NSLog(@"调用了 initWithCoder:");
}
return self;
}
这样虽然能够实现People
的归档和解档,但如果People
类有许多个属性,那么我们需要对每个属性都实现一遍encodeObject:forKey:
和decodeObjectForKey:
方法,这样就会显得比较繁琐,但是我们可以利用Runtime
获取所有属性来重写归档解档方法。如下:
- 创建一个
NSObject
的分类@interface NSObject (Runtime):
.h
中声明两个归档解档的方法:
@interface NSObject (Runtime)
- (void)encoder:(NSCoder *)aCoder;
- (void)decoder:(NSCoder *)aDecoder;
@end
.m
中利用Runtime重写归档解档的方法:
- (void)encoder:(NSCoder *)aCoder {
unsigned int count = 0;
Ivar * ivars = class_copyIvarList([self class], &count);
for (int i=0; i<count; i++) {
Ivar ivar = ivars[i];
NSString * key = [NSString stringWithUTF8String:ivar_getName(ivar)];
id value = [self valueForKey:key];
[aCoder encodeObject:value forKey:key];
}
//在OC中使用了Copy、Creat、New类型的函数,需要释放指针!(注:ARC管不了C函数)
free(ivars);
}
- (void)decoder:(NSCoder *)aDecoder {
unsigned int count = 0;
Ivar * ivars = class_copyIvarList([self class], &count);
for (int i=0; i<count; i++) {
Ivar ivar = ivars[i];
NSString * key = [NSString stringWithUTF8String:ivar_getName(ivar)];
id value = [aDecoder decodeObjectForKey:key];
[self setValue:value forKey:key];
}
free(ivars);
}
并在NSCoding
的协议方法中分别调用这两个方法:
#pragma mark - 实现NSCoding的协议方法
- (void)encodeWithCoder:(NSCoder *)aCoder {
//需要归档的属性
[self encoder:aCoder];
NSLog(@"调用了 encodeWithCoder:");
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self) {
//解档
[self decoder:aDecoder];
NSLog(@"调用了 initWithCoder:");
}
return self;
}
这样在分类中调用的好处是其他遵循了NSCoding
协议的对象想要实现归档和解档只需要导入头文件#import "NSObject+Runtime.h”
并在NSCoding
的协议方法中调用[self encoder:aCoder];
和[self decoder:aDecoder];
即可。
- 在
ViewController
中去调用People
的归档和解档方法:
People * p = [[People alloc]init];
p.time = @"今天";
p.dataArray = @[@"encode",@"decode"].copy;
//归档
[NSKeyedArchiver archiveRootObject:p toFile:self.filePath];
//解档
People * people = [NSKeyedUnarchiver unarchiveObjectWithFile:self.filePath];
NSLog(@"解档:%@,%@",people.time,people.dataArray);
打印结果为:
2018-08-03 17:07:51.586114+0800 WXQNSCoding_Runtime[7699:318222] 调用了 encodeWithCoder:
2018-08-03 17:07:51.587473+0800 WXQNSCoding_Runtime[7699:318222] 调用了 initWithCoder:
2018-08-03 17:07:51.587695+0800 WXQNSCoding_Runtime[7699:318222] 解档:今天,(
encode,
decode
)
我们也可以把实现NSCoding
的协议方法定义为一个宏,这样我们一个宏K_NSCODING_METHOD
就搞定了NSCoding
的协议方法的实现:
#define K_NSCODING_METHOD \
- (void)encodeWithCoder:(NSCoder *)aCoder { \
[self encoder:aCoder]; \
NSLog(@"调用了 encodeWithCoder:"); \
} \
\
- (instancetype)initWithCoder:(NSCoder *)aDecoder { \
self = [super init]; \
if (self) { \
[self decoder:aDecoder]; \
NSLog(@"调用了 initWithCoder:"); \
} \
return self; \
} \
Demo地址:Runtime实现iOS对象的归档和解档
网友评论