一、数据持久化概述
数据持久化就是数据的永久存储。其本质是将数据保存为文件,存到程序的沙盒中。
1、数据持久化的方式
1.1 writeToFile:简单对象写入文件
1.2 NSUserDefaults:应用程序偏好设置
1.3 Sqlite:轻量级关系型数据库,不能直接存储对象(NSData除外),需要用到一些SQL语句,先将复杂对象归档(对象->NSData)
1.4 CoreData:对象型数据库,实质是将数据库的内部存储细节封装
1.5 Plist文件
2、应用程序沙盒
每一应用程序都有自己的应用沙盒,沙盒的本质就是一个文件夹,名字是随机分配的。
与其他应用程序沙盒隔离,应用程序本身只能访问自己沙盒的数据。(iOS8+对沙盒之间的访问部分开放)
2.1 应用程序包(.app)
包含了应用程序中所用到的所有资源文件和可执行文件(Base on Unix)。iOS8时,app不存储在沙盒中,有单独的文件夹存储所有程序的app包。
2.2 HomeDirectory
Documents:保存应用运行时生成的需要持久化的数据,iTunes同步设备时会备份该目录。例如,游戏应用可将游戏存档保存在该目录
tmp:保存应用运行时所需的临时数据,使用完毕后再将相应的文件从该目录删除。应用没有运行时,系统也可能会清除该目录下的文件。iTunes同步设备时不会备份该目录
Library/Caches:保存应用运行时生成的需要持久化的数据,iTunes同步设备时不会备份该目录。一般存储体积大、不需要备份的非重要数据
Library/Preference:保存应用的所有偏好设置,iOS的Settings(设置)应用会在该目录中查找应用的设置信息。iTunes同步设备时会备份该目录
2.3 获取沙盒路径
沙盒根目录: NSHomeDirectory();
沙盒临时目录:NSTemporaryDirectory();
Library/Preferences:NSUserDefaults
//1.获取沙盒中Documents文件夹的路径
//第一种方式:
NSString *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject; //NO, path = @"~/"(相对路径); YES 绝对路径
NSLog(@"%@", documentPath);
//第二种方式: (不建议采用,因为新版本的操作系统可能会修改目录名)
NSString *homePath = NSHomeDirectory();
NSString *documentPath2 = [homePath stringByAppendingPathComponent:@"library/caches"];
NSLog(@"%@", documentPath2);
//2.获取应用程序包路径(.app)
NSLog(@"%@", [NSBundle mainBundle].resourcePath);
二、简单对象持久化
1、简单对象
NSString\NSArray\NSDictionary\NSData
使用writeToFile:方法,将数据存储为.plist文件
atomically参数为是否写入缓存
//字符串
NSString *string = @"I ? U";
//数组
NSArray *array = @[@"张三", @"李四", @"王五"];
//字典
NSDictionary *dictionary = @{@"name":@"张三", @"age":@"20", @"sex":@"男"};
//NSData
UIImage *image = [UIImage imageNamed:@"1.jpg"];
NSData *data = UIImageJPEGRepresentation(image, 1);
//1.拼接存储路径
NSString *strPath = [documentPath stringByAppendingPathComponent:@"string.txt"];
NSString *arrayPath = [documentPath stringByAppendingPathComponent:@"array.txt"];
NSString *dicPath = [documentPath stringByAppendingPathComponent:@"dict.txt"];
NSString *dataPath = [documentPath stringByAppendingPathComponent:@"data.txt"];
//2.写入文件
[string writeToFile:strPath atomically:YES encoding:NSUTF8StringEncoding error:nil];
[array writeToFile:arrayPath atomically:YES];
[dictionary writeToFile:dicPath atomically:YES];
[data writeToFile:dataPath atomically:YES];
//3.读取文件内容
NSString *fileString = [NSString stringWithContentsOfFile:strPath encoding:NSUTF8StringEncoding error:nil];
NSArray *fileArray = [NSArray arrayWithContentsOfFile:arrayPath];
NSDictionary *fileDict = [NSDictionary dictionaryWithContentsOfFile:dicPath];
NSData *fileData = [NSData dataWithContentsOfFile:dataPath];
NSLog(@"%@", fileString);
NSLog(@"%@", fileArray);
NSLog(@"%@", fileDict);
NSLog(@"%@", fileData);
2、文件管理类:NSFileManager
2.1、功能
NSFileManager使用defaultManager创建单例对象。可以创建文件夹,可以删除、移动、创建文件,判断文件是否存在。
2.2、使用
//缓存文件夹所在路径
NSString *cachesPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).lastObject;
NSLog(@"%@", cachesPath);
//在cachesPath路径下创建一个文件夹
NSString *directoryPath = [cachesPath stringByAppendingPathComponent:@"path"];
NSFileManager *fileManager = [NSFileManager defaultManager]; //创建文件管理类单例对象
//根据路径创建文件夹
NSDictionary *fileDate = @{@"createTime":@"2015-9-9"};
[fileManager createDirectoryAtPath:directoryPath withIntermediateDirectories:YES attributes:fileDate error:nil];
//根据路径创建文件(只能写入NSData类型的数据)
[fileManager createFileAtPath:directoryPath contents:data attributes:fileDate];
//删除文件
[fileManager removeItemAtPath:dicPath error:nil]; //删除~/documents/dict.txt
3、NSUserDefaults
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; //单例
[defaults setValue:@"yfyfyfyfyfyfyfy" forKey:@"username"];
[defaults setValue:@"123" forKey:@"password"];
//注意:UserDefaults设置数据时,不是立即写入,而是根据时间戳定时地把缓存中的数据写入本地磁盘。所以调用了set方法之后数据有可能还没有写入磁盘应用程序就终止了。出现以上问题,可以通过调用synchornize方法强制写入到文件中
[defaults synchronize];
//读取
NSString *name = [defaults valueForKey:@"username"];
NSString *pwd = [defaults valueForKey:@"password"];
二、复杂对象持久化(NSKeyedArchiver)
1、复杂对象
复杂对象是在Foundation框架内不存在的数据类,无法通过writeToFile写入到文件内,且至少包含一个实例对象。
由于复杂对象无法通过writeToFile:方法写入文件,只能将复杂对象转化为NSData对象,再进行数据持久化。
2、NSCoding协议
@protocol NSCoding
- (void)encodeWithCoder:(NSCoder *)aCoder;
- (id)initWithCoder:(NSCoder *)aDecoder; // NS_DESIGNATED_INITIALIZER
@end
3、复杂对象写入文件
Person.h
//复杂对象归档 一:遵守NSCoding协议
@interface Person : NSObject<NSCoding>
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, retain) NSString *name;
@property (nonatomic, retain) NSString *gender;
@end
Person.m
#import "Person.h"
@implementation Person //实现NSCoding协议
#pragma mark -- 进行编码 --
- (void)encodeWithCoder:(NSCoder *)coder
{
// [super encodeWithCode:coder]; 如果父类也遵守了NSCoding协议,确保继承的实例变量也能被编码,即也能被归档
[coder encodeObject:self.name forKey:@"name"];
[coder encodeInteger:self.age forKey:@"age"];
[coder encodeObject:self.gender forKey:@"gender"];
}
#pragma mark -- 进行解码 --
- (id)initWithCoder:(NSCoder *)aDecoder
{
// self = [super initWithCoder:aDecoder]; 确保继承的实例变量也能被解码,即也能被恢复
self = [super init];
if (self) {
self.name = [aDecoder decodeObjectForKey:@"name"];
self.gender = [aDecoder decodeObjectForKey:@"gender"];
self.age = [aDecoder decodeIntegerForKey:@"age"];
}
return self;
}
@end
ViewController.m
类方法进行编码\解码(只能归档一个对象):
NSString *objPath = [cachesPath stringByAppendingPathComponent:@"person.txt"];
[NSKeyedArchiver archiveRootObject:person toFile:objPath];
Person *p2 = [NSKeyedUnarchiver unarchiveObjectWithFile:objPath];
实例方法(可以归档多个对象):
#pragma mark -- 对复杂对象进行持久化(归档\编码) --
//过程:(复杂对象->归档->NSData->writeToFile:)
Person *person = [[Person alloc] init];
person.name = @"yf";
person.age = 20;
person.gender = @"man";
NSMutableData *mtData = [NSMutableData data];
//创建归档器
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:mtData];
//进行归档
[archiver encodeObject:person forKey:@"person"];
//***结束归档
[archiver finishEncoding];
//将归档之后的mtData写入文件
NSString *personPath = [cachesPath stringByAppendingPathComponent:@"person.txt"];
[mtData writeToFile:personPath atomically:YES];
NSLog(@"%@", personPath);
NSLog(@"%@", mtData);
#pragma mark -- 从文件中读取复杂对象(反归档\恢复\解码) --
//过程:(读取文件(NSData)->反归档->复杂对象)
//读取
NSData *readData = [NSData dataWithContentsOfFile:personPath];
//创建反归档工具
NSKeyedUnarchiver *unArchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:readData];
//使用反归档工具对readData进行反归档
Person *readPerson = [unArchiver decodeObjectForKey:@"person"];
4、使用NSKeyedArchive进行深复制
比如对一个Person对象进行深复制
// 临时存储person1的数据
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:person1];
// 解析data,生成一个新的Person对象
Student *person2 = [NSKeyedUnarchiver unarchiveObjectWithData:data];
// 分别打印内存地址
NSLog(@"person1:0x%x", person1); // person1:0x7177a60
NSLog(@"person2:0x%x", person2); // person2:0x7177cf0