Objective C中的文件归档 Objective C中对象的复制 iOS编程中常用的设计模式 Objective C中的文件归档 内容概述 概述 使用属性列表plists 保存数据 使用NSKeyedArchiver归档 归档自定义类型 利用归档实现深复制 16.1 概述 所谓对象归档是指将对象的状态以某种数据格式并以文件的方式,持久的保存下来。需要时再将文件内容转换为对象。这两个过程叫做归档或反归档。其他语言也叫序列化或者反序列化,例如,Java语言、c++语言中。 归档的数据文件格式可以是xml文件格式,也可以是key-value键值对的格式。xml文件格式也叫属性列表简称plist。使用key-value键值对的方式存取需要使用NSKeyedArchiver类来实现。自定义的类需要实现NSCoding协议,并实现协议方法才能进行归档,也可以利用归档实现对象的深复制。 16.2 使用属性列表plists 保存数据 如果要归档的数据是如下类型,NSArray NSString NSDictionary NSData NSNumber,可以使用writeToFile方法和xxxWithContentsOfFile方法来读写文件,xxx表示数据类型,例如arrary、string、dictionary和data等。 如果归档的是NSArray和NSDictionary类型,则以xml文件的格式保存。下面针对字符串、数组和字典的读写来详细讲述。 字符串的读写使用,- (BOOL)writeToFile:(NSString )path atomically:(BOOL)useAuxiliaryFile encoding:(NSStringEncoding)enc error:(NSError **)error方法和+ (id)stringWithContentsOfFile:(NSString )path encoding:(NSStringEncoding)enc error:(NSError **)error方法。 atomically:参数是是否保存临时文件。 encoding:字符编码 -(void)writeString{ // 声明字符串 NSString *str = @"Hello World!"; // 将字符串内容,写到tmp目录下的abc.txt文件中 [str writeToFile:@"/tmp/abc.txt" atomically:YES encoding:NSUTF8StringEncoding error:nil]; } -(void)readString{ // 获得tmp目录下,abc.txt中的内容 NSString *str = [NSString stringWithContentsOfFile:@"/tmp/abc.txt" encoding:NSUTF8StringEncoding error:nil]; NSLog(@"%@",str); } 数组和字典的读写和字符串类似,只不过保存的文件格式是一个xml格式。 -(void)writeArray{ // 声明数组 NSArray *array = [NSArray arrayWithObjects:@"one",@"two", nil]; // 将数组内容,写到tmp目录下的abc02.txt文件中 [array writeToFile:@"/tmp/abc02.txt" atomically:TRUE]; } -(void)readArray{ // 读tmp目录下的abc02.txt文件中的内容 NSArray *array = [NSArray arrayWithContentsOfFile:@"/tmp/abc02.txt"]; // 遍历数组内容 for(NSString *item in array){ NSLog(@"item=%@",item); } } -(void)writeDictionary{ // 声明字典 NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:@"tom",@"1",@"kite",@"2",@"rose",@"3", nil]; // 将字典内容写到tmp目录下的def.txt文件中 [dic writeToFile:@"/tmp/def.txt" atomically:TRUE]; } -(void)readDictionary{ // 读取tmp目录下的def.txt文件中的内容 NSDictionary *dic = [NSDictionary dictionaryWithContentsOfFile:@"/tmp/def.txt"]; // 遍历字典 for(NSString *key in dic){ NSLog(@"[%@,%@]",key,[dic objectForKey:key]); } } 我们可以到文件系统查看一下生成的文件内容。 16.3 使用NSKeyedArchiver 除了可以归档NSArray NSString NSDictionary NSData NSNumber这些类型,我们也可以使用NSKeyedArchiver和NSKeyedUnarchiver来以键值对的方式归档对象。这种方式归档的对象每个属性都用一个键来标记,这样就可以随机读取内容,而不是按照写入的顺序来读取。 归档方法使用+ (BOOL)archiveRootObject:(id)rootObject toFile:(NSString )path,而反归档方法使用+ (id)unarchiveObjectWithFile:(NSString )path方法。下面的程序代码演示了这两个过程。 -(void)archive{ // 声明一个字典 NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:@"tom",@"1",@"kite",@"2",@"rose",@"3", nil]; // 归档字典到tmp目录下的test.archive文件中 [NSKeyedArchiver archiveRootObject:dic toFile:@"/tmp/test.archive"]; } -(void)unarchive{ // 声明一个字典 NSDictionary *dic2 = [NSKeyedUnarchiver unarchiveObjectWithFile:@"/tmp/test.archive"]; // 解归档 for(NSString *key in dic2){ NSLog(@"%@:%@",key,[dic2 objectForKey:key]); } } 程序输出结果如下: 2013-04-15 15:20:42.744 chapter16-01[1257:303] 1:tom 2013-04-15 15:20:42.746 chapter16-01[1257:303] 2:kite 2013-04-15 15:20:42.747 chapter16-01[1257:303] 3:rose 16.4 归档自定义类型 如果要归档的对象类型是自定义类型例如Person类型,那么我们需要实现NSCoding协议,并实现- (void)encodeWithCoder:(NSCoder )aCoder和- (id)initWithCoder:(NSCoder )aDecoder方法。 NSCoding中定义了一组编解码方法。编码方法如下所示。 - (void)encodeObject:(id)objv forKey:(NSString *)key; - (void)encodeConditionalObject:(id)objv forKey:(NSString *)key; - (void)encodeBool:(BOOL)boolv forKey:(NSString *)key; - (void)encodeInt:(int)intv forKey:(NSString *)key; - (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:(const uint8\_t *)bytesp length:(NSUInteger)lenv forKey:(NSString *)key; 该组方法中的第一个参数是不同的数据类型值,第二个参数是键值。 解码方法如下所示。 - (id)decodeObjectForKey:(NSString *)key; - (BOOL)decodeBoolForKey:(NSString *)key; - (int)decodeIntForKey:(NSString *)key; - (int32\_t)decodeInt32ForKey:(NSString *)key; - (int64\_t)decodeInt64ForKey:(NSString *)key; - (float)decodeFloatForKey:(NSString *)key; - (double)decodeDoubleForKey:(NSString *)key; 该组方法可以根据key获得value。 下面是一个Person类实现了NSCoding协议进行归档的代码。 // 实现NSCoding协议 @interface Person : NSObject <NSCopying,NSCoding> { int age; NSString *name; } @property int age; @property (nonatomic,copy) NSString *name; @end 实现编解码方法。 // 编码 -(void)encodeWithCoder:(NSCoder *)aCoder{ [aCoder encodeInt:age forKey:@"age"]; [aCoder encodeObject:name forKey:@"name"]; } // 解码 -(id)initWithCoder:(NSCoder *)aDecoder{ age = [aDecoder decodeIntForKey:@"age"]; name = [aDecoder decodeObjectForKey:@"name"]; return self; } 测试Person归档。 -(void)custom{ // 实例化Person Person *per = [[Person alloc]init]; // 设置属性 [per setAge:20]; [per setName:@"tom"]; // 归档Person [NSKeyedArchiver archiveRootObject:per toFile:@"/tmp/person.archive"]; // 反归档Person Person *per2 = [NSKeyedUnarchiver unarchiveObjectWithFile:@"/tmp/person.archive"]; // 输出person属性 NSLog(@"name=%@,age=%d",[per2 name],[per2 age]); } 16.5 利用归档实现深复制 学习了对象归档后,利用对象归档可以实现对象的深复制。我们可以使用NSKeyedArchiver的+ (NSData )archivedDataWithRootObject:(id)rootObject方法将对象归档为NSData,再使用NSKeyedUnarchiver的+ (id)unarchiveObjectWithData:(NSData )data方法进行反归档即可实现对象的深复制。 -(void)deepCopy{ // 声明可变数组 NSMutableArray *array = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"one"],[NSMutableString stringWithString:@"two"], nil]; // 将数组转换为NSData NSData *buffer; buffer = [NSKeyedArchiver archivedDataWithRootObject:array]; // 反归档 NSMutableArray *array2 = [NSKeyedUnarchiver unarchiveObjectWithData:buffer]; // 测试结果 NSMutableString *element = [array2 objectAtIndex:0]; [element appendString:@" changed"]; for(NSString *item in array){ NSLog(@"%@",item); } for(NSString *item in array2){ NSLog(@"%@",item); } } Objective C中对象的复制 iOS编程中常用的设计模式