在 iOS 中,有很多途径可以将对象写入磁盘,其中最重要的一种途径是“固化”(archiving):固化是将单个或多个对象从内存写入文件系统的过程。“解固”(unarchiving)是从文件读取对象并写回内存。
固化可以通过 NSCoder 实例来完成, NSCoder 实例本身就是一个数据容器,可以存放对象和对象的数据,一旦 NSCoder 收集齐所需的数据,就可以将其写入指定的文件系统中的文件。
不是所有对象都可以固化 - 只有遵守 NSCoding 协议的对象才可以。
NSCoding 协议有两个方法(都是必须的方法):encodeWithCoder:(固化用);initWithCoder(解固用)
一, 对象的固化
通过 NSKeyedArchiver 的类方法 archiveRootObject:toFile :可以将遵守 NSCoding 协议的对象写入文件。NSKeyedArchiver 是 NSCoder 的子类。
archiveRootObject:toFile: 方法的第一个参数是根对象(root Object);第二个参数是写入文件的路径
// 将传入的文件名加在目录后并返回
// archiverRootObject:toFile:方法会创建一个 NSKeyedArchiver 实例,然后向 allObjects 发送 encodeWithCoder 消息,并将新创建的 NSKeyedArchiver 对象作为参数传入。 固化数组时,数组中的对象也会被固化(前提是这些对象也遵守 NSCoding 协议)
return [NSKeyedArchiver archiveRootObject:allObjects toFile:objectsPath];
如图:
在 encodeWithCoder 中,通过向 NSCoder 对象(这里是 NSKeyedArchiver 对象)发送 encodeObject:forkey: 消息,可以对对象的实例变量编码(encode)
- (void)encodeWithCoder:(NSCoder *)encoder { // 根据变量名固化实例变量 // 如果实例变量是对象,则该对象也会受到 encodeWithCoder: 消息 [encoder encodeObject:objectName1 forkey:@"objectName1"]; [encoder encodeObject:objectName2 forkey:@"objectName2"] // 基本类型 [encode encodeInt:int1 forkey:@"int1"]; }
这类固化称为“有键固化”(keyed archiving),有键固化的工作机制与 NSMutableDictionary 非常类似:通过加入对象,并用同一个键取出对象,有键固化使用的键都是 NSString 实例,通常是需要编码的实例变量的变量名
如果传入 encodeWithCoder: 的实例对象是对象,那么该对象也会受到 encodeWithCoder:消息,所以固化是一个递归过程
二, 对象的解固
应用通过“解固”来载入固化后的对象,首先,程序会创建一个 NSCoder 实例,然后从文件系统读入数据,最后解固固化后的对象。
通过 NSKeyedUnarchiver 的类方法 unarchiveObjectWithFile: 可将数据从文件系统还原至内存。
NSKeyedUnarchiver 也是 NSCoder 的子类。 unarchiveObjectWithFile: 的参数是固化文件的路径,它会将相应的文件读入内存,然后开始一系列的解固过程
// 如果allObjects数组不存在,则从文件取之 // 注意,unarchiveObjectWithFile: 的返回值类型为 id if(!allObjects) { NSString *path= obejctsPath; allObjects = [[NSKeyedUnarchiver unarchiveObjectWithFile:path] retain]; }
解固的根对象为 allObjects ,类型为NSMutaleArray,NSMutableArray 的 initWithCoder: 方法会向每个已固化对象发送 decodeObjectForKey: 消息,从而解码数组中的内容
- (id)initWithCoder:(NSCoder *)decoder { self = [super init]; if (self) { [self setObjectName1:[decoder decodeObjectForKey:@"objectName1"]]; [self setObjectName2:[decoder decodeObjectForKey:@"objectName2"]]; [self setint1:[decoder decodeIntForKey:@"int1"]] ; } return self ; }