归档与解档是iOS中序列化与反序列化的方式,需要实现 encodeWithCoder 和 initWithCoder 方法,实现方式有两种:第一种是分别为属性赋值;第二种是通过runtime机制,循环为属性赋值。
归档和解档:即将数据写入文件和从文件中读取数据。
此处以plist文件为例说明,
一、plist文件使用时的注意事项:
1.plist文件中仅支持写入Array,Dictionary,Boolean,Data,Date,Number,String类型。
2.如果想要将自定义类的对象数据写入plist文件,则需要将对象修改为NSData
类型写入。
二、归档步骤:
1.自定义的类必须遵守NSCoding
协议,重写其归档和解档的方法。
- (void)encodeWithCoder:(NSCoder *)aCoder;
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder;
2.使用[NSKeyedArchiver archivedDataWithRootObject:p1]方法将p1对象转化为NSData类型。
3.通过NSArray调用arrayWithObjects方法写入plist文件中。
三、解档步骤:
1.从plist文件中读取内容
2.调用 [NSKeyedUnarchiver unarchiveObjectWithData:arr[i]]方法将NSData对象转化为Person对象。
新建Teacher类
@interface Teacher : NSObject
@property (nonatomic, strong) NSString *grade;//年级
@property (nonatomic, assign) NSInteger studentCount;//学生人数
@end
调用序列化与反序列化方法
- (void)save {
Teacher *t = [[Teacher alloc] init];
t.grade = @"1年3班";
t.studentCount = 40;
NSString *plistFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"t.data"];
[NSKeyedArchiver archiveRootObject:t toFile:plistFilePath];
}
- (void)get {
NSString *plistFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"t.data"];
Teacher *t = [NSKeyedUnarchiver unarchiveObjectWithFile:plistFilePath];
NSLog(@"%@,%ld",t.grade, t.studentCount);
}
下面实现一下 encodeWithCoder 和 initWithCoder 方法。
第一种方式:
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:self.grade forKey:@"grade"];
[coder encodeInteger:self.studentCount forKey:@"studentCount"];
}
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super init];
if (self) {
_grade = [coder decodeObjectForKey:@"grade"];
_studentCount = [coder decodeIntegerForKey:@"studentCount"];
}
return self;
}
当属性很多或者新增属性时,这两个方法就显得庞大而且不利于维护,同时还要注意属性的类型,类型不同调用的方法也不同。如:encodeObject 和 encodeInteger。
第二种方式:需要导入 #import
- (void)encodeWithCoder:(NSCoder *)coder {
unsigned int numberOfIvars = 0;
//成员变量
Ivar *ivars = class_copyIvarList([Teacher class], &numberOfIvars);
for (const Ivar *p = ivars; p < ivars + numberOfIvars; p++) {
Ivar const ivar = *p;
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
[coder encodeObject:[self valueForKey:key] forKey:key];
}
free(ivars);
}
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super init];
if (self) {
unsigned int numberOfIvars = 0;
//成员变量
Ivar *ivars = class_copyIvarList([Teacher class], &numberOfIvars);
for (const Ivar *p = ivars; p < ivars + numberOfIvars; p++) {
Ivar const ivar = *p;
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
[self setValue:[coder decodeObjectForKey:key] forKey:key];
}
free(ivars);
}
return self;
}
这种方式通过runtime机制,循环遍历属性进行操作,非常方便,不用维护。网上好多文章也是这么写的,貌似没有问题。
注意:当有以下这种继承关系的时候就有问题了。创建Person类,Teacher继承Person。
@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end
@interface Teacher : Person
@property (nonatomic, strong) NSString *grade;
@property (nonatomic, assign) NSInteger studentCount;
@end
此时序列化并不会保存父类的属性,只需对上面的代码稍作修改就可以了。
- (void)encodeWithCoder:(NSCoder *)coder {
Class cls = [self class];
while (cls != [NSObject class]) {
unsigned int numberOfIvars = 0;
//成员变量
Ivar *ivars = class_copyIvarList([cls class], &numberOfIvars);
for (const Ivar *p = ivars; p < ivars + numberOfIvars; p++) {
Ivar const ivar = *p;
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
[coder encodeObject:[self valueForKey:key] forKey:key];
}
free(ivars);
cls = class_getSuperclass(cls);
}
}
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super init];
if (self) {
Class cls = [self class];
while (cls != [NSObject class]) {
unsigned int numberOfIvars = 0;
//成员变量
Ivar *ivars = class_copyIvarList([cls class], &numberOfIvars);
for (const Ivar *p = ivars; p < ivars + numberOfIvars; p++) {
Ivar const ivar = *p;
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
[self setValue:[coder decodeObjectForKey:key] forKey:key];
}
free(ivars);
cls = class_getSuperclass(cls);
}
}
return self;
}
————————————————
原文链接:https://blog.csdn.net/junjie_zhang/article/details/100537797