Objective C中的文件归档


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);

    }

}