对象的归档
在某种情况下,我们可能有这样的需求,需要将程序中用到的多个对象及其属性值,以及它们的相互对应关系保存到文件中,或者发送到另外的进程。为了实现这个功能,Foundation
框架中,可以把互相关联的多个对象归档为二进制文件,并且还可以将对象的关系从二进制文件内还原出来。像这样,将对象打包成二进制文件就称为归档。
Foundation框架的归档功能
将对象存储转换为二进制序列的过程称为归档、打包或编码,逆变换则称为解档或解码或对象还原。
Foundation
框架中的归档和系统架构是相互独立的。即PowerPC 和Intel都可以利用。在对象包含的实例变量值中,整数及实数这样的基本数据类型,以及指向其他对象的指针等都可以归档。普通指针虽然不能归档,但是根据指向的数据类型有时也可以归档的。
可以使用 NSKeyedArchiver
和 NSKeyedUnarchiver
完成对象的归档和解档操作,而它们都是抽象类 NSCoder
的子类。
所有可以归档的对象都必须要适用于NSCoding
。协议NSCoding
在Foundation/NSObject.h
中定义,NSObject 自身并不采用该协议。NSString
、NSDictionary
等Foundation
框架的主要类都适用协议NSCoding
。
协议NSCoding
按照如下方式声明。
@protocol NSCoding
- (void)encodeWithCoder:(NSCoder *)coder;
- (nullable instancetype)initWithCoder:(NSCoder *)coder; // NS_DESIGNATED_INITIALIZER
@end
归档方法的定义
协议NSCoding
中,函数encodeWithCoder
定义了归档自身的方法。参数coder是传人的实例,NSKeyedArchiver
的实例在 Foundation/NSKeyedArchiver.h
中。
- (void)encodeWithCoder:(NSCoder *)coder
{
// coder encodeObject:对象 forKey:关键词字符串
// coder encodeInteger:整数变量 forKey:关键词字符串
//可选数据类型有多种
[coder encodeObject:self.url forKey:@"url"];
}
如果超类适用于协议NSCoding
,那么 super 将调用encodeWithCoder
对超类的实例变量进行归档。如果超类像NSObject
一样不适用于协议NSCoding
,则不可调用。
解档方法的定义
为了从归档中还原对象,需要在各类定义初始化方法,即协议NSCoding
的方法initWithCoder
。参数对象为coder,实际上是继承于NSCoder
的NSKeyedArchiver
实例变量。这个对象被称解档器。 NSKeyedUnarchiver
的接口在Foundation/NSKeyedArchiver.h
中定义。
- (instancetype)initWithCoder:(NSCoder *)coder
{
//self=[super initWithCoder:coder]; 超类不适用于协议NSCoding,使用[super init]
self=[super init];
if(self){
//变量=[coder decodeObjectForKey:键值];
self.url=[coder decodeObjectForKey:@"url"];
}
return self;
}
只需在编码和解码中指定相同的键值,解码即可按照任意顺序还原对象,即使有不能还原的变量也无妨。
编码方法
//将字符串作为键值来对参数对象编码
- (void)encodeObject:(nullable id)object forKey:(NSString *)key;
//在对象图中需要参数object时将字符串作为键值编码
- (void)encodeConditionalObject:(nullable id)object forKey:(NSString *)key;
//将字符串作为键值对整数参数归档
- (void)encodeInt:(int)value forKey:(NSString *)key;
解码方法
//使用指定键值来还原编码对象
- (nullable id)decodeObjectForKey:(NSString *)key;
//将键值为参数字符串的整数解码
- (int)decodeIntForKey:(NSString *)key;
归档和解档初始化方法
NSKeyedArchiver
的实例,即归档器。可将对象编码结果写入数据对象内。NSKeyedArchiver
的初始化方法为:
//将预先生成的NSMutableData 实例作为参数,初始化NSKeyedArchiver 实例。参数的数据对象被保存下来。最终生成归档。
- (instancetype)initForWritingWithMutableData:(NSMutableData *)data;
归档器生成后,就可使用上面介绍的encode...方法进行数据归档。编码根对象后,对象图全体会被递归地进行归档。
所有归档完成后,最后必须调用finishEncoding
方法进行后处理。
- (void)finishEncoding;
处理完成后,因归档器初始化传人的对象已经进行了归档。即可将其保存到文件中,或向其他进程发送消息。
解档器NSKeyedUnarchiver
的初始化方法
//初始化接收器,将参数对象从归档中还原,参数data被保存在接收器内
- (nullable instancetype)initForReadingFromData:(NSData *)data error:(NSError **)error;
还原归档的对象图和还原一个对象是同样的,都使用方法decodeObjectForKey:
。
以上介绍了归档和解档实例生成方法,还有更简洁的方法也可将归档结果写入文件内,进行读取与还原。
//NSKeyedArchiver 类中的方法,通过指定根对象将归档结果写入指定路径的文件中。返回值为YES表示成功
+ (BOOL)archiveRootObject:(id)rootObject toFile:(NSString *)path;
//NSKeyedUnarchiver 类中方法,从指定路径中读出归档数据进行解档,返回根对象。处理失败则返回nil。
+ (nullable id)unarchiveObjectWithFile:(NSString *)path;
举例说明
@interface ProductModel : NSObject<NSCoding>
@property (nonatomic,copy)NSString *url;
@end
#import "ProductModel.h"
@implementation ProductModel
- (instancetype)initWithCoder:(NSCoder *)coder
{
//self=[super initWithCoder:coder]; 超类不适用于协议NSCoding,使用[super init]
self=[super init];
if(self){
self.url=[coder decodeObjectForKey:@"url"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder
{
[coder encodeObject:self.url forKey:@"url"];
}
@end
- (void)createProducts{
ProductModel *product=[[ProductModel alloc]init];
product.url=@"https://www.baidu.com/";
NSString*cachePath =NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask,YES).firstObject;
NSString *path=[cachePath stringByAppendingPathComponent:@"product"];
NSLog(@"path:%@",path);
[NSKeyedArchiver archiveRootObject:product toFile:path];
id object=[NSKeyedUnarchiver unarchiveObjectWithFile:path];
ProductModel *productModel=object;
NSLog(@"productModel.url:%@",productModel.url);
}
//打印日志为:
2021-12-29 16:09:28.443771+0800 OCTest3[496:38974] path:/var/mobile/Containers/Data/Application/E3E1CDF0-AF9D-4D0B-9425-806439F2AA0C/Library/Caches/product
2021-12-29 16:09:28.463214+0800 OCTest3[496:38974] productModel.url:https://www.baidu.com/
网友评论