数据持久化一般有四张方式,上面我们说了使用NSUserDefaults进行数据持久化,下面我们总结一下使用归档的方式进行数据持久化
归档是一种很常用的文件储存方法,几乎任何类型的对象都能够被归档储存(实际上是一种文件保存的形式)
使用NSKeyedArchiver进行归档、NSKeyedUnarchiver进行接档,这种方式在写入、读出数据之前对数据进行序列化、反序列化操作
一、对简单对象归档
可以使用archiveRootObject对简单对象进行归档
//归档 NSString *homedirectory = NSHomeDirectory(); self.archivefilepath = [homedirectory stringByAppendingPathComponent:@"object.archiver"]; NSString *homepath =[homedirectory stringByAppendingPathComponent:@"my.archiver"];//扩展名可以是任意的 //归档,可以对字符串、数字进行归档,当然了一可以对NSArray,NSDictionary进行归档,归档成功返回YES,失败返回NO BOOL flag = [NSKeyedArchiver archiveRootObject:@"zhangsan" toFile:homepath]; if(flag){ NSLog(@"数据归档成功"); }else{ NSLog(@"数据归档失败"); } //接档 NSString *username = [NSKeyedUnarchiver unarchiveObjectWithFile:homepath]; NSLog(@"反归档数据:%@",username);
二、对多个对象归档
对多个对象归档同样是使用NSKeyedArchiver,但是需要在归档的时候使用encodeXXX方法进行归档,最后通过writeToFile方法写入文件
//归档,写数据 CGPoint point = CGPointMake(10,10); NSString *desc =@"坐标起始点"; NSInteger val = 10; NSArray *stuArray =[NSArray arrayWithObjects:@"xiaozhang",@"xiaowang",@"Sandy", nil]; NSDictionary *stuInfoDic =[NSDictionary dictionaryWithObjectsAndKeys:@"22",@"age",@"male",@"sex", nil]; NSString *multipath =[homedirectory stringByAppendingPathComponent:@"multi.archiver"]; NSMutableData *mutabledata =[[NSMutableData alloc] init]; NSKeyedArchiver *archiver =[[NSKeyedArchiver alloc] initForWritingWithMutableData:mutabledata]; //对多个文档对象对象归档 [archiver encodeCGPoint:point forKey:@"kpoint"]; [archiver encodeObject:desc forKey:@"kdesc"]; [archiver encodeInteger:val forKey:@"kval"]; [archiver encodeObject:stuArray forKey:@"kstu"]; [archiver encodeObject:stuInfoDic forKey:@"kstuinfo"]; [archiver finishEncoding]; [mutabledata writeToFile:multipath atomically:YES]; //接档,读数据 NSMutableData *unarchiverData =[[NSMutableData alloc] initWithContentsOfFile:multipath]; NSKeyedUnarchiver *unarchiver =[[NSKeyedUnarchiver alloc] initForReadingWithData:unarchiverData]; CGPoint newpoint =[unarchiver decodeCGPointForKey:@"kpoint"]; NSString *newdesc =[unarchiver decodeObjectForKey:@"kdesc"]; NSInteger newval =[unarchiver decodeIntegerForKey:@"kval"]; NSArray *stuNames =[unarchiver decodeObjectForKey:@"kstu"]; NSDictionary *stuInfo =(NSDictionary *)[unarchiver decodeObjectForKey:@"kstuinfo"]; [unarchiver finishDecoding]; NSLog(@"point:%f,%f",newpoint.x,newpoint.y); NSLog(@"desc:%@",newdesc); NSLog(@"val:%d",newval); NSLog(@"Stu Name:%@",stuNames); NSLog(@"Stu Info:%@",stuInfo);
三、对自定义对象归档
从上面归档的对象来看都是基本数据类型,那么能不能对自己定义类的实例对象进行归档呢,答案是肯定的,在开发过程中我们经常需要用到自定义对象,如MVC中的model层
下面我们来说说对自定义对象进行归档,要实现对自定义对象的归档与接档,需要实现NSCoding与NSCopying协议(具体协议可以参考apple官方文档)
首先新建一个Person对象,继承自NSObject,实现NSCoding与NSCopying协议,定义一些属性,具体代码如下
Person.h
#define kName @"kname" #define kAge @"kage" #define kPhone @"kphone" #define kphoto @"kphoto" #import <Foundation/Foundation.h> @interface Person : NSObject<NSCoding,NSCopying> @property(nonatomic,copy)NSString *name; @property NSInteger age; @property (nonatomic,copy)NSString *phone; @property(nonatomic,copy)UIImage *photo; @end
Person.m
#import "Person.h" @implementation Person @synthesize name = _name; @synthesize age = _age; @synthesize phone = _phone; @synthesize photo = _photo; -(id)initWithCoder:(NSCoder *)aDecoder{ if(self =[super init]){ _name =[aDecoder decodeObjectForKey:kName]; _age =[aDecoder decodeIntegerForKey:kAge]; _phone = [aDecoder decodeObjectForKey:kPhone]; _photo =[aDecoder decodeObjectForKey:kphoto]; } return self; } -(void)encodeWithCoder:(NSCoder *)aCoder{ [aCoder encodeObject:_name forKey:kName]; [aCoder encodeInteger:_age forKey:kAge]; [aCoder encodeObject:_phone forKey:kPhone]; [aCoder encodeObject:_photo forKey:kphoto]; } -(id)copyWithZone:(NSZone *)zone{ Person *copy = [[[self class] allocWithZone:zone] init]; copy.name =[self.name copyWithZone:zone]; copy.age = self.age; copy.phone = [self.phone copyWithZone:zone]; copy.photo =self.photo; return copy; } @end
Person定义了四个字段(名称,年龄,电话,照片)
Person类实现了三个委托方法
//从coder中读取数据,保存到相应的变量中,即反序列化数据
- (id)initWithCoder:(NSCoder *)decoder
;// 读取实例变量,并把这些数据写到coder中去。序列化数据
- (void)encodeWithCoder:(NSCoder *)encoder
//返回这个对象一个新的实例
- (id)copyWithZone:(NSZone *)zone
备注:要将一个自定义对象进行归档,那么这个自定义对象里面的每个属性都是可以被归档的,如果是不能归档的类型,我们可以把他转化为NSValue进行归档,然后在读出来的时候在转化为相应的类。
我们来对Person对象进行归档与接档,首先我们来新建一个Project,新建一个Person类,在UIViewController上面拖放两个Button,一个UIImageView,在UIViewController的类文件中定义一些属性与方法,代码如下:
#define kPersonData @"kpersondata"//归档键值 #import <UIKit/UIKit.h> #import "Person.h" @interface LTViewController : UIViewController -(IBAction)BtnClickArchiver:(id)sender;//归档事件 -(IBAction)BtnClickUnArchiver:(id)sender;//接档事件 @property(nonatomic,strong)IBOutlet UIImageView *headimageview;//头像显示 @property(nonatomic,strong)NSString *archivefilepath;//归档的路径 @end
// // LTViewController.m // DataArchiverDemo // // Created by admin on 14-10-14. // Copyright (c) 2014年 com.doubleicon. All rights reserved. // #import "LTViewController.h" @interface LTViewController () @end @implementation LTViewController @synthesize archivefilepath; - (void)viewDidLoad { NSString *homedirectory = NSHomeDirectory(); self.archivefilepath = [homedirectory stringByAppendingPathComponent:@"object.archiver"]; NSString *homepath =[homedirectory stringByAppendingPathComponent:@"my.archiver"];//扩展名可以是任意的 //归档,可以对字符串、数字进行归档,当然了一可以对NSArray,NSDictionary进行归档,归档成功返回YES,失败返回NO BOOL flag = [NSKeyedArchiver archiveRootObject:@"zhangsan" toFile:homepath]; if(flag){ NSLog(@"数据归档成功"); }else{ NSLog(@"数据归档失败"); } //反归档 NSString *username = [NSKeyedUnarchiver unarchiveObjectWithFile:homepath]; NSLog(@"反归档数据:%@",username); CGPoint point = CGPointMake(10,10); NSString *desc =@"坐标起始点"; NSInteger val = 10; NSArray *stuArray =[NSArray arrayWithObjects:@"xiaozhang",@"xiaowang",@"Sandy", nil]; NSDictionary *stuInfoDic =[NSDictionary dictionaryWithObjectsAndKeys:@"22",@"age",@"male",@"sex", nil]; NSString *multipath =[homedirectory stringByAppendingPathComponent:@"multi.archiver"]; NSMutableData *mutabledata =[[NSMutableData alloc] init]; NSKeyedArchiver *archiver =[[NSKeyedArchiver alloc] initForWritingWithMutableData:mutabledata]; //对多个文档对象对象归档 [archiver encodeCGPoint:point forKey:@"kpoint"]; [archiver encodeObject:desc forKey:@"kdesc"]; [archiver encodeInteger:val forKey:@"kval"]; [archiver encodeObject:stuArray forKey:@"kstu"]; [archiver encodeObject:stuInfoDic forKey:@"kstuinfo"]; [archiver finishEncoding]; [mutabledata writeToFile:multipath atomically:YES]; //反归档 NSMutableData *unarchiverData =[[NSMutableData alloc] initWithContentsOfFile:multipath]; NSKeyedUnarchiver *unarchiver =[[NSKeyedUnarchiver alloc] initForReadingWithData:unarchiverData]; CGPoint newpoint =[unarchiver decodeCGPointForKey:@"kpoint"]; NSString *newdesc =[unarchiver decodeObjectForKey:@"kdesc"]; NSInteger newval =[unarchiver decodeIntegerForKey:@"kval"]; NSArray *stuNames =[unarchiver decodeObjectForKey:@"kstu"]; NSDictionary *stuInfo =(NSDictionary *)[unarchiver decodeObjectForKey:@"kstuinfo"]; [unarchiver finishDecoding]; NSLog(@"point:%f,%f",newpoint.x,newpoint.y); NSLog(@"desc:%@",newdesc); NSLog(@"val:%d",newval); NSLog(@"Stu Name:%@",stuNames); NSLog(@"Stu Info:%@",stuInfo); [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } //自定义对象的归档 -(void)BtnClickArchiver:(id)sender{ Person *person =[[Person alloc] init]; person.name = @"doubleicon"; person.age = 23; person.photo = [UIImage imageNamed:@"boy"]; person.phone = @"13896459863"; NSMutableData *persondata =[[NSMutableData alloc] init]; NSKeyedArchiver *archiver =[[NSKeyedArchiver alloc] initForWritingWithMutableData:persondata]; [archiver encodeObject:person forKey:kPersonData]; [archiver finishEncoding]; [persondata writeToFile:self.archivefilepath atomically:YES]; NSLog(@"归档对象成功"); } //自定义对象的接档 -(void)BtnClickUnArchiver:(id)sender{ NSData *persondata =[[NSData alloc] initWithContentsOfFile:self.archivefilepath]; NSKeyedUnarchiver *unarchiver =[[NSKeyedUnarchiver alloc] initForReadingWithData:persondata]; Person *person =[unarchiver decodeObjectForKey:kPersonData]; [unarchiver finishDecoding]; NSString *name =person.name; NSInteger age =person.age; NSString *phone = person.phone; UIImage *image = person.photo; self.headimageview.image = image; NSLog(@"Name:%@,Age:%d,Phone:%@",name,age,phone); NSLog(@"反归档对象成功"); } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end
总结:与属性列表相比,归档可以写入复杂对象