概述
- 归档与恢复完全类似于其他语言中的序列化和反序列化
- 归档(序列化)是把对象转换为可保存、可传输的数据流
- 恢复(反序列化)是从数据流中恢复对象
NSCoding
@protocol NSCoding
- (void)encodeWithCoder:(NSCoder *)aCoder;
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder; // NS_DESIGNATED_INITIALIZER
@end
@interface FBAnimal : NSObject <NSCoding>
- (instancetype)initWithAge:(int)age andColor:(int)color andWeight:(double)weight;
@property (nonatomic) int age;
@property (nonatomic) int color;
@property (nonatomic) double weight;
@end
@implementation FBAnimal
- (instancetype)initWithAge:(int)age andColor:(int)color andWeight:(double)weight
{
self = [super init];
if(self)
{
self.age = age;
self.color = color;
self.weight = weight;
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeInt:self.age forKey:@"age"];
[aCoder encodeInt:self.color forKey:@"color"];
[aCoder encodeDouble:self.weight forKey:@"weight"];
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder;
{
self.age = [aDecoder decodeIntForKey:@"age"];
self.color = [aDecoder decodeIntForKey:@"color"];
self.weight = [aDecoder decodeDoubleForKey:@"weight"];
return self;
}
-(void)info
{
NSLog(@"age = %d", self.age);
NSLog(@"color = %d", self.color);
NSLog(@"weight = %f", self.weight);
}
@end
NSKeyedArchiver
- (void)encodeObject:(nullable id)objv forKey:(NSString *)key;
- (void)encodeConditionalObject:(nullable id)objv forKey:(NSString *)key;
- (void)encodeBool:(BOOL)boolv forKey:(NSString *)key;
- (void)encodeInt:(int)intv forKey:(NSString *)key; // native int
- (void)encodeInt32:(int32_t)intv forKey:(NSString *)key;
- (void)encodeInt64:(int64_t)intv forKey:(NSString *)key;
- (void)encodeFloat:(float)realv forKey:(NSString *)key;
- (void)encodeDouble:(double)realv forKey:(NSString *)key;
- (void)encodeBytes:(nullable const uint8_t *)bytesp length:(NSUInteger)lenv forKey:(NSString *)key;
encode各种数据类型,encodeObject本质就是调用object的encodeWithCoder:
NSKeyedUnarchiver
- (nullable id)decodeObjectForKey:(NSString *)key;
- (BOOL)decodeBoolForKey:(NSString *)key;
- (int)decodeIntForKey:(NSString *)key; // may raise a range exception
- (int32_t)decodeInt32ForKey:(NSString *)key;
- (int64_t)decodeInt64ForKey:(NSString *)key;
- (float)decodeFloatForKey:(NSString *)key;
- (double)decodeDoubleForKey:(NSString *)key;
- (nullable const uint8_t *)decodeBytesForKey:(NSString *)key returnedLength:(nullable NSUInteger *)lengthp NS_RETURNS_INNER_POINTER; // returned bytes immutable, and they go away with the unarchiver, not the containing autorelease pool
decode各种数据类型,decodeObjectForKey的本质就是调用object的initWithCoder:
归档和恢复单一对象
NSKeyedArchiver
+ (NSData *)archivedDataWithRootObject:(id)rootObject;
+ (BOOL)archiveRootObject:(id)rootObject toFile:(NSString *)path;
分析:
- 前者归档(序列化)单一对象到NSData
- 后者归档(序列化)单一对象到文件中
- 上面方法的本质是生成一个NSKeyedArchiver实例对象,然后调用rootObject的encodeWithCoder:,把生成的NSKeyedArchiver实例对象作为参数传入,在encodeWithCoder:中完成归档(序列化)到NSData中,只不过前者直接返回NSData,后者则多了一个步骤把NSData写入本地文件
NSKeyedUnarchiver
+ (nullable id)unarchiveObjectWithData:(NSData *)data;
+ (nullable id)unarchiveObjectWithFile:(NSString *)path;
分析:
- 前者恢复(反序列化)NSData到单一对象
- 后者恢复(反序列化)文件到单一对象
- 上面方法的本质是生成一个NSKeyedUnarchiver实例对象(data作为数据传入),再生成一个要恢复的目标对象(data中保存了类型相关信息,根据类型相关信息生成目标实例对象),然后调用目标对象的initWithCoder:,把生成的NSKeyedUnarchiver实例对象作为参数传入,在initWithCoder:中完成恢复(反序列化),最后返回恢复(反序列化)完成的目标实例对象,前者直接使用NSData传入序列化数据,后者则多了一个步骤把文件读入到NSData
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docDir = [paths objectAtIndex:0];
NSString *file = [NSString stringWithFormat:@"%@/%@", docDir, @"animal.archive"];
FBAnimal *animal = [[FBAnimal alloc] initWithAge:5 andColor:8 andWeight:58];
[NSKeyedArchiver archiveRootObject:animal toFile:file];
FBAnimal *otherAnimal = [NSKeyedUnarchiver unarchiveObjectWithFile:file];
NSLog(@"animal = %p", animal);
[animal info];
NSLog(@"otherAnimal = %p", otherAnimal);
[otherAnimal info];
output:
animal = 0x7f95a1d4efe0
age = 5
color = 8
weight = 58.000000
otherAnimal = 0x7f95a1d52700
age = 5
color = 8
weight = 58.000000
注:animal和otherAnimal指向不同的实例对象,但是内容相同
结论:
- 归档(序列化)对象的本质是调用对象的encodeWithCoder:
- 恢复(反序列化)对象的本质是调用对象的initWithCoder:
- 对象要支持归档(序列化)和恢复(反序列化)必须实现NSCoding protocol
归档和恢复任意多个对象
NSKeyedArchiver
- (instancetype)initForWritingWithMutableData:(NSMutableData *)data;
分析:
- 使用一个NSMutableData类型作为参数传入,然后不断encodeXXX:forKey:要归档(序列化)的数据
NSKeyedUnarchiver
- (instancetype)initForReadingWithData:(NSData *)data;
分析:
- 使用一个NSData(已经序列化的源数据)类型作为参数传入,然后不断调用decodeXXX:forKey:要恢复(反序列化)的数据
NSMutableData *mutableData = [NSMutableData data];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:mutableData];
FBAnimal *animal1 = [[FBAnimal alloc] initWithAge:15 andColor:18 andWeight:158];
FBAnimal *animal2 = [[FBAnimal alloc] initWithAge:25 andColor:28 andWeight:258];
FBAnimal *animal3 = [[FBAnimal alloc] initWithAge:35 andColor:38 andWeight:358];
int animalNum = 3;
NSLog(@"mutableData length = %lu", mutableData.length);
[archiver encodeInt:animalNum forKey:@"animalNum"];
[archiver encodeObject:animal1 forKey:@"animal1"];
[archiver encodeObject:animal2 forKey:@"animal2"];
[archiver encodeObject:animal3 forKey:@"animal3"];
NSLog(@"mutableData length = %lu", mutableData.length);
[archiver finishEncoding];
NSLog(@"mutableData length = %lu", mutableData.length);
NSLog(@"animalNum = %d", animalNum);
NSLog(@"animal1 = %p, animal2 = %p, animal3 = %p", animal1, animal2, animal3);
[animal1 info];
[animal2 info];
[animal3 info];
NSData *data = [mutableData copy];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
data = nil;
int otherAnimalNum = [unarchiver decodeIntForKey:@"animalNum"];
FBAnimal *otherAnimal3 = [unarchiver decodeObjectForKey:@"animal3"];
FBAnimal *otherAnimal2 = [unarchiver decodeObjectForKey:@"animal2"];
FBAnimal *otherAnimal1 = [unarchiver decodeObjectForKey:@"animal1"];
NSLog(@"otherAnimalNum = %d", otherAnimalNum);
NSLog(@"otherAnimal1 = %p, otherAnimal2 = %p, otherAnimal3 = %p", otherAnimal1, otherAnimal2, otherAnimal3);
[otherAnimal1 info];
[otherAnimal2 info];
[otherAnimal3 info];
[unarchiver finishDecoding];
output:
mutableData length = 0
mutableData length = 0
mutableData length = 396
animalNum = 3
animal1 = 0x7fc7a0f505c0, animal2 = 0x7fc7a0f4ef30, animal3 = 0x7fc7a0f4fc90
age = 15
color = 18
weight = 158.000000
age = 25
color = 28
weight = 258.000000
age = 35
color = 38
weight = 358.000000
otherAnimalNum = 3
otherAnimal1 = 0x7fc7a0f544f0, otherAnimal2 = 0x7fc7a0f544d0, otherAnimal3 = 0x7fc7a0f54350
age = 15
color = 18
weight = 158.000000
age = 25
color = 28
weight = 258.000000
age = 35
color = 38
weight = 358.000000
分析:
- 归档(序列化)的时候,先把encode的数据放入NSKeyedArchiver实例对象自身的缓存,只有调用finishEncoding后,才会把NSKeyedArchiver的数据缓存copy到NSMutableData
- 恢复(反序列化)的时候,先把NSData的数据缓存copy到NSKeyedUnarchiver实例对象自身的缓存,然后对NSKeyedUnarchiver实例对象自身的缓存decode,finishDecode结束decode
深复制应用
FBAnimal *animal1 = [[FBAnimal alloc] initWithAge:15 andColor:18 andWeight:158];
FBAnimal *animal2 = [[FBAnimal alloc] initWithAge:25 andColor:28 andWeight:258];
NSDictionary *animalDict = [NSDictionary dictionaryWithObjectsAndKeys:animal1, @"harry", animal2, @"tom", nil];
NSData *animalData = [NSKeyedArchiver archivedDataWithRootObject:animalDict];
NSDictionary *otherAnimalDict = [NSKeyedUnarchiver unarchiveObjectWithData:animalData];
FBAnimal *otherAnimal1 = [otherAnimalDict objectForKey:@"harry"];
FBAnimal *otherAnimal2 = [otherAnimalDict objectForKey:@"tom"];
NSLog(@"animalDict = %p, animal1 = %p, animal2 = %p", animalDict, animal1, animal2);
[animal1 info];
[animal2 info];
NSLog(@"otherAnimalDict = %p, otherAnimal1 = %p, otherAnimal2 = %p", otherAnimalDict, otherAnimal1, otherAnimal2);
[otherAnimal1 info];
[otherAnimal2 info];
output:
animalDict = 0x7fc1e95c41d0, animal1 = 0x7fc1e95c0160, animal2 = 0x7fc1e95c24c0
age = 15
color = 18
weight = 158.000000
age = 25
color = 28
weight = 258.000000
otherAnimalDict = 0x7fc1e95c62d0, otherAnimal1 = 0x7fc1e95c5b80, otherAnimal2 = 0x7fc1e95c5f60
age = 15
color = 18
weight = 158.000000
age = 25
color = 28
weight = 258.000000