NSCoding 与归档解档
NSCoding 是一个协议,用于支持对象的序列化和反序列化,也就是“归档”和“解档”操作。归档的过程将对象保存为数据,可以存储到磁盘、数据库或者传输到网络,而解档是将这些数据恢复为原始的对象。
NSCoding 协议
NSCoding
协议有两个主要方法:
encodeWithCoder:
: 将对象的状态编码成数据,这个方法在归档过程中被调用。initWithCoder:
: 从数据中解码并还原对象的状态,这个方法在解档时被调用。
为什么使用 NSCoding?
- 数据持久化:将对象的状态保存到文件中,使得在应用关闭后仍然能恢复这些对象的状态。
- 数据传输:通过网络发送对象时,可以先将对象归档成二进制格式,再进行传输。
- 缓存:将对象缓存到磁盘,以便下次使用,避免重复计算或请求。
如何实现 NSCoding?
要让一个类支持归档和解档,必须遵守 NSCoding
协议,具体实现两个方法:encodeWithCoder:
和 initWithCoder:
。
示例
假设我们有一个 Person
类,需要将其归档并解档:
1. Person 类实现 NSCoding 协议
#import <Foundation/Foundation.h>
@interface Person : NSObject <NSCoding>
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age;
@end
@implementation Person
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age {
self = [super init];
if (self) {
_name = name;
_age = age;
}
return self;
}
#pragma mark - NSCoding
// 归档(编码)
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:self.name forKey:@"name"];
[coder encodeInteger:self.age forKey:@"age"];
}
// 解档(解码)
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super init];
if (self) {
_name = [coder decodeObjectForKey:@"name"];
_age = [coder decodeIntegerForKey:@"age"];
}
return self;
}
@end
2. 使用 NSKeyedArchiver 和 NSKeyedUnarchiver 进行归档和解档
// 创建一个 Person 对象
Person *person = [[Person alloc] initWithName:@"Alice" age:30];
// 归档:将 Person 对象转换成 NSData,并保存到文件中
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:person];
// 保存到文件
NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"person.archive"];
[data writeToFile:filePath atomically:YES];
// 解档:从文件中读取数据并恢复成 Person 对象
NSData *savedData = [NSData dataWithContentsOfFile:filePath];
Person *decodedPerson = [NSKeyedUnarchiver unarchiveObjectWithData:savedData];
// 输出解档后的对象信息
NSLog(@"Name: %@, Age: %ld", decodedPerson.name, (long)decodedPerson.age);
3. 解释代码
- 归档:
- 使用
NSKeyedArchiver
将Person
对象转化为NSData
。其中,encodeWithCoder:
方法会被自动调用,将对象的属性保存到数据中。 - 归档后的数据可以保存到文件、数据库或发送到网络。
- 使用
- 解档:
- 使用
NSKeyedUnarchiver
将NSData
解析为Person
对象。此时,initWithCoder:
方法被调用,解码过程会根据保存时的key
恢复对象的状态。
- 使用
实际应用场景
-
用户数据持久化:
- 当用户退出应用时,保存用户信息(如用户名、头像、登录状态等)到本地,当再次启动应用时,恢复这些信息。
-
缓存数据:
- 将网络请求得到的结果(如图片、文章内容等)进行归档,缓存到本地,下次启动应用时直接读取缓存,减少网络请求。
-
对象序列化与反序列化:
- 在 iOS 应用中,常常需要将对象数据存储到
UserDefaults
或数据库中,使用归档可以非常方便地将对象数据存储和恢复。
- 在 iOS 应用中,常常需要将对象数据存储到
注意事项
-
归档对象需要遵守 NSCoding 协议:只有实现了
encodeWithCoder:
和initWithCoder:
的类才支持归档。 -
对象中引用的其他对象也需要遵守 NSCoding 协议:如果一个对象的属性是另一个对象(比如
Person
中的address
是一个Address
对象),那么Address
类也需要实现NSCoding
,否则在归档和解档时会出错。 -
避免归档非必要属性:对于不需要归档的属性(比如临时计算值、私有变量等),可以在归档过程中忽略。可以通过
@property
和@dynamic
来排除一些字段的归档。 -
iOS 10 之后,归档和解档的 API 被弃用:
- iOS 10 后,
NSKeyedArchiver
和NSKeyedUnarchiver
被一些新的方法所替代(如NSKeyedArchiver.archivedData(withRootObject:)
),为了避免未来的兼容性问题,建议使用新的归档 API。
- iOS 10 后,