iOS数据持久化存储:归档
在平时的iOS开发中,我们经常用到的数据持久化存储方式大概主要有:NSUserDefaults(plist),文件,数据库,归档。。前三种比较经常用到,第四种归档我个人感觉用的还是比较少的,恰恰因为用的比较少,但是还是有地方要用到,所以今天再把归档解档复习一遍吧。
一、什么是归档:
对象归档是将对象以文件的形式保存到磁盘中(也称为序列化,持久化) ;使用的时候读取该文件的保存路径读取文件的内容(也称为解档,反序列化)。
二、归档 与 plist存储的区别:
- 对象归档的文件是保密的,在磁盘上无法查看文件中的内容,而plist属性列表是明文的可以查看。
- 其次就是保存的数据类型不同
- 只要是对象类型,归档都可以保存。
- 而plist只能保存这几种类型:NSString NSNumber NSDate NSData NSArray NSDictionary 。不能保存其他的类型
三、归档的使用
刚刚说到,只要是对象类型,都可以用归档的方式进行保存。
但是,只有实现了<NSCoding>协议的类型才可以进行归档,由于Foudation框架中的对象类型已经实现了NSCoding协议,所以可以直接归档解档,而我们自定义的类型,则需要我们手动去实现NSCoding协议。必须实现一下两个方法:
//归档的时候调用的方法 - (void)encodeWithCoder:(NSCoder *)aCoder; //解归档的时候要调用的函数 - (id)initWithCoder:(NSCoder *)aDecoder;
1.对系统类的归档:
第一种方式:单个对象的归档和解档
//单个对象归档: NSArray *array1 = @[@"zhangsan",@"wangwu",@"lisi"]; NSString *filePath = [NSHomeDirectory() stringByAppendingPathComponent:@"array.src"]; BOOL success = [NSKeyedArchiver archiveRootObject:array1 toFile:filePath]; if (success) { NSLog(@"保存成功!"); } //单个对象解档 id array2 = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath]; NSLog(@"%@",array2);
第二种方式:多个对象的归档和解档
//多个对象归档: NSArray *array1 = @[@"zhangsan",@"wangwu",@"lisi"]; NSMutableData *data = [NSMutableData data]; NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; //编码 [archiver encodeObject:array1 forKey:@"array"]; [archiver encodeInt:100 forKey:@"scope"]; [archiver encodeObject:@"jack" forKey:@"name"]; //完成编码,讲上面的归档数据填充到data中,此时data已经存储了归档对象的数据 [archiver finishEncoding]; NSString *filePath = [NSHomeDirectory() stringByAppendingPathComponent:@"array.src"]; BOOL success = [data writeToFile:filePath atomically:YES]; if (success) { NSLog(@"归档成功"); } //多个对象解档 NSData *data2 = [[NSData alloc] initWithContentsOfFile:filePath]; NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data2]; NSArray *array = [unarchiver decodeObjectForKey:@"array"]; NSLog(@"%@",array); int value = [unarchiver decodeIntForKey:@"scope"]; NSLog(@"%d",value);
[unarchiver finishDecoding];
2.自定义类型的归档解档
上面已经说过了,对系统类型可以直接进行归档和解档,但是对于自定义类型,必须实现NSCoding协议
#import <Foundation/Foundation.h> #import "Car.h" int main(int argc, const char * argv[]) { /* 有辆汽车 有一个引擎 和 四个轮胎 然后需要对这个汽车进行归档 本地保存数据 这时 还必须要对汽车的引擎和轮胎也要归档 只要有成员变量是对象地址 都要继续归档 那么这时归档的类 都要遵守协议NSCoding 实现里面的方法 实际上 归档就是一个深拷贝 */ @autoreleasepool { #if 0 Car *BMW = [[Car alloc] init]; BMW.speed = 100; [BMW run]; //程序结束之前把汽车对象的信息进行保存到本地 //归档 NSString *filePath = [NSHomeDirectory() stringByAppendingPathComponent:@"car.src"]; BOOL ret = [NSKeyedArchiver archiveRootObject:BMW toFile:filePath]; if (ret) { NSLog(@"汽车对象归档成功"); }else { NSLog(@"汽车归档失败"); } [BMW release]; #else //解归档-->从文件中获取数据创建一个Car对象 NSString *filePath = [NSHomeDirectory() stringByAppendingPathComponent:@"car.src"]; Car *car = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath]; NSLog(@"car.speed:%ld",car.speed); NSLog(@"engine.name:%@",car.engine.name); [car run]; #endif } return 0; }
汽车类:
#import <Foundation/Foundation.h> #import "Engine.h" #import "Tyre.h" //遵守归档协议 @interface Car : NSObject<NSCoding> { Engine *_engine; NSMutableArray *_tyresArr; NSInteger _speed; } @property (nonatomic) NSInteger speed; @property (nonatomic,retain) Engine *engine; - (Car *)init; - (void)run; @end
#import "Car.h" @implementation Car - (void)dealloc { NSLog(@"汽车销毁"); [_engine release]; [_tyresArr release]; [super dealloc]; } //对Car归档的时候会调用的方法 - (void)encodeWithCoder:(NSCoder *)aCoder { //就是对成员变量的数据进行归档 //如果成员变量是基本类型 那么直接归档 //如果成员变量变量是对象地址 那么对象的类也要遵守协议实现方法 //在类中 如果有setter和getter方法那么尽量使用setter和getter方法 [aCoder encodeInteger:self.speed forKey:@"speed"]; //对一个对象地址指向的对象进行归档 那么指向对象也要实现归档协议的方法 Engine 要遵守 数组要遵守 数组的元素也要遵守 [aCoder encodeObject:self.engine forKey:@"engine"]; [aCoder encodeObject:_tyresArr forKey:@"array"]; } //对象解归档、读档的时候调用 - (id)initWithCoder:(NSCoder *)aDecoder { if (self = [super init]) {//如果父类有initWithCoder那么调用父类的initWithCoder: //这里不要直接写 成员变量 否则就有可能崩溃 //要用setter方法 这样 内部会计数器+1 拥有绝对使用权 //读档 self.engine = [aDecoder decodeObjectForKey:@"engine"]; NSLog(@"%ld",self.engine.retainCount); self.speed = [aDecoder decodeIntegerForKey:@"speed"]; //要注意拥有绝对使用权 _tyresArr = [[aDecoder decodeObjectForKey:@"array"] retain]; } return self; } - (Car *)init { if (self = [super init]) { _engine = [[Engine alloc] init]; _engine.name = @"德国"; _engine.power = 1000; _tyresArr = [[NSMutableArray alloc] init]; for (int i = 0; i < 4; i++) { Tyre *tyre = [[Tyre alloc] init]; tyre.name = @"米其林"; tyre.type = 12345; [_tyresArr addObject:tyre]; [tyre release]; } } return self; } - (void)run { NSLog(@"汽车跑"); [self.engine start]; for (Tyre *tyre in _tyresArr) { [tyre scroll];//滚动 } } @end
轮胎类:
#import <Foundation/Foundation.h> @interface Tyre : NSObject<NSCoding> { NSString *_name; NSInteger _type; } @property (nonatomic,copy)NSString *name; @property (nonatomic)NSInteger type; - (void)scroll; @end
#import "Tyre.h" @implementation Tyre - (void)dealloc { NSLog(@"轮胎销毁"); self.name = nil; [super dealloc]; } //归档调用 - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:self.name forKey:@"name"]; [aCoder encodeInteger:self.type forKey:@"type"]; } //读档调用 - (id)initWithCoder:(NSCoder *)aDecoder { if (self = [super init]) { //用setter方赋值 内部有计数器+1 self.name = [aDecoder decodeObjectForKey:@"name"]; self.type = [aDecoder decodeIntegerForKey: @"type"]; } return self; } - (void)scroll { NSLog(@"%@ 轮胎在滚动",self.name); } @end
引擎类:
#import <Foundation/Foundation.h> @interface Engine : NSObject <NSCoding> { NSString *_name; NSInteger _power; } @property (nonatomic,copy)NSString *name; @property (nonatomic) NSInteger power; - (void)start; @end
#import "Engine.h" @implementation Engine - (void)dealloc { NSLog(@"引擎销毁"); self.name = nil; [super dealloc]; } //归档调用 - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:self.name forKey:@"name"]; [aCoder encodeInteger:self.power forKey:@"power"]; } //读档调用 - (id)initWithCoder:(NSCoder *)aDecoder { if (self = [super init]) { self.name = [aDecoder decodeObjectForKey:@"name"]; self.power = [aDecoder decodeIntegerForKey: @"power"]; } return self; } - (void)start { NSLog(@"引擎启动"); } @end