数据持久化及数据的永久存储,将数据保存在硬盘中,程序关闭,内存释放后,重新打开程序,可以继续访问之前保存的数据。
常见的数据持久化方式有以下几项:
这篇只讲沙盒,preference,归档/反归档。
沙盒是系统为每一个应用程序生成的一个特定文件夹 文件夹的名字由十六进制数据组成,每一个应用程序的沙盒文件名都是不一样的,是由系统随机生成的。
//获取沙盒主目录 NSString *path = NSHomeDirectory(); NSLog(@"%@",path);
沙盒下每个文件夹的路径及作用
//Documents 存放的一些比较重要的文件,但是存入Documents中的文件不能过大 //如何获取Documents文件目录 NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; NSLog(@"%@",documentsPath); //用firstobject取值是因为该方法一开始使用mac端OS X开发,对于PC端用户可以有多个用户,所以可以取到很多user的路径,但是该方法现在用于手机开发,手机端只有一个用户,所以获得的用户只有一个,lastobject也是可以的。 //Library:是一个资源库,存储一些不太重要的数据,相对比较大一些,里边有两个子文件夹; NSString *libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES)firstObject]; //Caches:缓存文件,图片缓存,音频,视频。网页资源。应用程序清除缓存,就是清除该文件夹 NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)firstObject]; //Preferences:系统偏好设置,用户对应程序的设置,比如用户名和用户密码,preference路径无法找到,通过NSUserDefaults //temp:存放临时文件,比如下载的压缩包zip,解压后理解把压缩包删除 NSString *tempPath = NSTemporaryDirectory(); NSLog(@"%@",tempPath); //bundle:ios8之前,包和沙河在同一个目录下,之后.app单独存储到一个独立的文件目录下。 .app 文件 readOnly。从appStore下载下来的是这个包,程序上传的时候也是这个包 NSString *bundlepath = [[NSBundle mainBundle] bundlePath]; NSLog(@"%@",bundlepath);
// NSSearchPathDirectory 这个类是用来查找文件目录的 //第一个参数:文件名称 //第二个参数,确定搜索域 //第三个参数:确定相对路径还是绝对路径。YES绝对,NO相对
文件相关操作
/** * 文件删除 */ - (void)deleteFile { NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject]; NSString *imagePath = [cachesPath stringByAppendingPathComponent:@"iamge"]; NSLog(@"%@",imagePath); //创建文件管理者 NSFileManager *fileManager = [NSFileManager defaultManager]; //判断文件是否存在 BOOL isExist = [fileManager fileExistsAtPath:imagePath]; if (!isExist) { BOOL isSuccess = [fileManager createDirectoryAtPath:imagePath withIntermediateDirectories:YES attributes:nil error:nil]; NSLog(@"%@",isSuccess ? @"创建成功" : @"创建失败"); } //删除文件 if ([fileManager fileExistsAtPath:imagePath]) { BOOL isSucess = [fileManager removeItemAtPath:imagePath error:nil]; NSLog(@"%@",isSucess ? @"删除成功" : @"删除失败"); } }
/** * 移动 */ - (void)moveFile { NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject]; NSString *imagePath = [cachesPath stringByAppendingPathComponent:@"iamge"]; NSLog(@"%@",imagePath); //创建文件管理者 NSFileManager *fileManager = [NSFileManager defaultManager]; //判断文件是否存在 BOOL isExist = [fileManager fileExistsAtPath:imagePath]; if (!isExist) { BOOL isSuccess = [fileManager createDirectoryAtPath:imagePath withIntermediateDirectories:YES attributes:nil error:nil]; NSLog(@"%@",isSuccess ? @"创建成功" : @"创建失败"); } //删除文件 // if ([fileManager fileExistsAtPath:imagePath]) { // BOOL isSucess = [fileManager removeItemAtPath:imagePath error:nil]; // NSLog(@"%@",isSucess ? @"删除成功" : @"删除失败"); // } //拷贝文件 // 把包中的plist文件拷贝到image文件夹下 NSString *plistInBundlePath = [[NSBundle mainBundle] pathForResource:@"NB.plist" ofType:nil]; NSString *nBPath = [imagePath stringByAppendingPathComponent:@"NB.plist"]; // 拷贝, if (![fileManager fileExistsAtPath:nBPath]) { BOOL isSuccess = [fileManager copyItemAtPath:plistInBundlePath toPath:nBPath error:nil]; NSLog(@"%@",isSuccess ? @"拷贝成功" : @"拷贝失败"); } NSString *toPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)firstObject]; BOOL isSuccess = [fileManager moveItemAtPath:nBPath toPath:[toPath stringByAppendingPathComponent:@"NB.plist"] error:nil]; NSLog(@"%@",isSuccess ? @"移动成功" : @"移动失败"); }
//字符串写入文件 - (void)writeToFile { //简单对象写入文件:字符串,数组,字典,二进制流 只有这些简单对象支持文件的写入 //如果要写入的文件数据时数组,字典,必须要保证数组,字典中的数据 也是简单对象(如果是复杂对象,请参照后边的归档,反归档) //将字符串写入Documents文件夹下 NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)firstObject]; NSString *toPath = [documentsPath stringByAppendingPathComponent:@"string.txt"]; NSString *string = @"string-string-string-"; //第一个参数,要写入的文件路径,如果不存在文件,会自动创建 第二个参数,原子性,判断是否需要生成辅助文件,保护在多线程下安全 第三个参数,编码格式 第四个参数,错误信息 NSError *error = nil; BOOL isSuccess = [string writeToFile:toPath atomically:YES encoding:NSUTF8StringEncoding error:&error]; NSLog(@"%@",isSuccess ? @"写入成功" : @"写入失败"); NSString *str = [NSString stringWithContentsOfFile:toPath encoding:NSUTF8StringEncoding error:nil]; NSLog(@"%@",str); }
//数组写入文件 - (void)writeArray { NSString *str = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES)firstObject]; NSString *toPath = [str stringByAppendingPathComponent:@"array.txt"]; NSArray *array = @[@"123",@"123"]; BOOL isSuccess = [array writeToFile:toPath atomically:YES]; NSLog(@"%@",isSuccess ? @"写入成功" : @"写入失败"); NSArray *arr = [NSArray arrayWithContentsOfFile:toPath]; NSLog(@"%@",arr); }
//字典写入文件 - (void)writeDic { NSDictionary *dic = @{@"key":@"value"}; NSString *str = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)firstObject]; NSString *toPath = [str stringByAppendingPathComponent:@"dictonry.txt"]; BOOL isSuccess = [dic writeToFile:toPath atomically:YES]; NSLog(@"%@",isSuccess ? @"成功" : @"失败"); NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:toPath]; NSLog(@"%@",dict); }
//NSData写入文件 - (void)writeData { NSString *tempPath = NSTemporaryDirectory(); NSString *toPath = [tempPath stringByAppendingPathComponent:@"data.txt"]; NSString *string = @"datadata"; NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding]; BOOL isSuccess = [data writeToFile:toPath atomically:YES]; NSLog(@"%@",isSuccess ? @"成功" : @"失败"); NSData *getData = [NSData dataWithContentsOfFile:toPath]; NSString *str = [[NSString alloc] initWithData:getData encoding:NSUTF8StringEncoding]; NSLog(@"%@",str); }
//Preference - (void)writeToPreference { // NSUserDefaults 继承自NSObject ,单例 通过kvc模式赋值 NSLog(@"%@",NSHomeDirectory()); //创建用户索引对象 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; [defaults setInteger:100 forKey:@"money"]; //立即同步操作 对preference中的文件进行修改后,立即同步 [defaults synchronize]; NSInteger money = [defaults integerForKey:@"money"]; NSLog(@"%ld",money); [defaults setInteger:10000 forKey:@"MyMoney"]; [defaults synchronize]; NSInteger you = [defaults integerForKey:@"you"]; NSLog(@"%ld",(long)you); if (money < 10) { NSLog(@"there is no money"); } else { NSLog(@"-100"); money -= 100; [defaults setInteger:40 forKey:@"money"]; [defaults setInteger:1000 forKey:@"YourMoney"]; } // NSUserDefaults 一般存储一些比较小的数据,大部分用来存数值 //例子:判断用户是否第一次登陆 NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults]; BOOL isFirst = [userDefault boolForKey:@"isFirst"]; [userDefault setBool:YES forKey:@"isFirst"]; [userDefault synchronize]; if (!isFirst) { NSLog(@"第一次登陆"); } else { NSLog(@"不是第一次登陆"); } }
复杂对象写入文件需要使用归档 ,读取需要使用反归档,不能直接写入文件,数组中有复杂对象也要使用归档 / 反归档
复杂对象使用行归档和反归档,需要遵循 NSCoding 协议,并实现协议中 - ( void )encodeWithCoder:( NSCoder *)aCoder; - ( id )initWithCoder:( NSCoder *)aDecoder; 两个方法
新建Person类
在Person.h中 遵循协议
// // Person.h // 07.24-DataPersistiser // // Created by lanouhn on 14/7/24. // Copyright (c) 2014年 LCD. All rights reserved. // #import <Foundation/Foundation.h> @interface Person : NSObject <NSCoding> @property (nonatomic, copy) NSString *name; @property (nonatomic, copy) NSString *gender; @property (nonatomic, assign) NSInteger age; @end
在Person.m中实现协议方法
// // Person.m // 07.24-DataPersistiser // // Created by lanouhn on 14/7/24. // Copyright (c) 2014年 LCD. All rights reserved. // #import "Person.h" @implementation Person //当要归档时,对象会自动触发这个方法 - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:self.name forKey:@"name"]; [aCoder encodeObject:self.gender forKey:@"gender"]; [aCoder encodeObject:@(self.age) forKey:@"age"]; } - (id)initWithCoder:(NSCoder *)aDecoder { self = [super init]; if (self) { self.name = [aDecoder decodeObjectForKey:@"name"]; self.gender = [aDecoder decodeObjectForKey:@"gender"]; self.age = [[aDecoder decodeObjectForKey:@"age"] integerValue]; } return self; } @end
归档 / 反归档
- (void)archiver { //把复杂文件写入文件,先转化为nsdata对象 //归档:归档的实质就是把其他类型的数据(person),先转化为NSData,再写入文件。 Person *person = [[Person alloc] initWithName:@"rose" gender:@"girl" age:25]; // NSKeyedArchiver 压缩工具类,继承自NSCoder,主要用于编码 NSMutableData *data = [NSMutableData data] ; NSKeyedArchiver *achiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; //使用压缩工具讲Person压到data中 [achiver encodeObject:person forKey:@"person"]; //完成压缩,停掉压缩工具 [achiver finishEncoding]; NSString *homePath = NSHomeDirectory(); NSString *toPath = [homePath stringByAppendingPathComponent:@"person.txt"]; BOOL isSuccess = [data writeToFile:toPath atomically:YES]; NSLog(@"%@",isSuccess ? @"成功" : @"失败"); //复杂对象的读取,反归档 // NSKeyedUnarchiver NSData *getData = [NSData dataWithContentsOfFile:toPath]; NSKeyedUnarchiver *unArchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:getData]; //解压 Person *p1 = [unArchiver decodeObjectForKey:@"person"]; [unArchiver finishDecoding]; }
//集合NSArray NSDictionary 如果想进行归档和反归档,那么它里边存储的元素也要遵循NSCoding协议 Person *person1 = [[Person alloc] initWithName:@"jack" gender:@"男" age:20]; Person *person2 = [[Person alloc] initWithName:@"rose" gender:@"女" age:20]; NSArray *array = @[person1,person2]; NSMutableData *data = [NSMutableData data]; NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; [archiver encodeObject:array forKey:@"array"]; [archiver finishEncoding]; NSString *tmpPath = NSTemporaryDirectory(); NSString *toPath = [tmpPath stringByAppendingString:@"array.txt"]; BOOL isSuccess = [data writeToFile:toPath atomically:YES]; NSLog(@"%@",isSuccess ? @"成功" : @"失败"); //反归档 NSKeyedUnarchiver *unArchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; NSArray *unArchiverArray = [unArchiver decodeObjectForKey:@"array"];