数据持久化的本质 - 数据保存成文件,存储到程序的沙盒中
-在应用程序结束时,将内存中的数据以文件的形式搬到(保存到)硬盘中
沙盒机制(Sand box):是个安全机制 -这就是ios和Mac系统安全的原因,就是因为它采用了沙盒机制
越狱之后沙盒机制被损坏了,应用程序之间可能会相互盗取信息
沙盒其实是个文件夹,名字是随机分配的,我们叫它UID(全球唯一标识符,只有这个应用程序自身知道这个ID,其他应用程序是不知道的)
一、获取沙盒下文件的路径
//1.获取当前应用程序的沙盒主路径(沙盒化的应用程序所能访问的文件夹路径只有自己所产生的沙盒文件夹下的相关子文件夹,包括:Documents, Library, tmp文件夹)
NSString *sandboxPath = NSHomeDirectory();
NSLog(@"%@",sandboxPath);
结果:/Users/xalo/Library/Developer/CoreSimulator/Devices/3A5016C9-61FD-4384-B8C8-D00FD78FAC4F/data/Containers/Data/Application/3936C752-77D1-439C-83D9-348189C81A0B
3A5016C9-61FD-4384-B8C8-D00FD78FAC4F 当前模拟器的串号
3936C752-77D1-439C-83D9-348189C81A0B 当前应用程序的沙盒文件夹
//2.获取临时文件夹路径tmp,tmp文件夹路径适合保存一些临时文件,不适合用于保存重要用户数据,因为当手机容量已满时系统会把所有应用它程序的tmp文件夹清空一遍
NSString*tmpPath= NSTemporaryDirectory(); //方法一
NSLog(@"%@",tmpPath);
NSString*tmpPath1= [sandboxPath stringByAppendingPathComponent:@"tmp"]; //方法二:stringByAppendingPathComponent:拼接字符串路径组件(组件就是文件夹或者文件名)- 会自带“/”
//3.通过路径搜索函数获取对应的Documents文件夹路径
//第一个参数:被搜索的文件夹目录枚举值
//第二个参数:搜索域 - 在iOS中只有NSUserDomainMask可用
//第三个参数:是否显示完整路径, YES表示返回完整路径
NSArray *paths= NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); //补充:生成的路径是唯一的,数组中只有一个元素 1.第一个参数:目录(路径)名称 2.第二个参数:指定搜索域(此处是user作用域) 严谨起见, 不要把域名指定成:NSAllDomainsMask, 而是指定成NSUserDomainMask 3.是否使用完整的路径名称(1)NO-~/Documents" (2)YES
NSString *documentsPath= paths.firstObject; //或者paths.lastObject
NSLog(@"doc---%@",documentsPath);
二、简单对象写入文件
//简单对象写入文件包含:NSString,NSArray,NSDictionary, NSData等类及其子类的对象写入到指定路径的文件
//1.指定对象将要存储的沙盒文件路径
//2.构造对应累的对象
//3.调用该类自己的writeToFile开头的写入文件的方法,将对象的内容写入到文件中保存在对应的沙盒路径下
举例:
//1.字符串对象写入文件
//(1).生成文本文件的存储路径
NSString *textPath = [documentsPath stringByAppendingPathComponent:@"text.txt"];
//(2).创建需要被保存的字符串对象
NSString *string= @"这是一条测试文本";
//(3).将字符串写入对应路径的文件
[string writeToFile:textPathatomically:YES encoding:NSUTF8StringEncoding error:nil];//automically保证多线程的安全,加锁解锁, 保证我写完之后,才可以做别的操作, error:一般写入不会有错, 置为nil
//2.数组对象写入文件中
//1.指定数组的文件存储路径
NSString *arrayPath= [documentsPath stringByAppendingPathComponent:@"array.plist"]; //plist=property list plist是苹果专用的配置文件,保存配置选项,本质是XML(Extensible markable language -可扩展标记语言)
//2.创建要写入文件的对象
NSArray *array = @[@"I", @"am", @"the", @"best", @"one", @"."];
//2.调用数组的writeToFile:atomically:方法将数组内容写入到文件中
[array writeToFile:arrayPath atomically:YES];
/*
用文本编辑器显示的结果
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plistPUBLIC"-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<string>I</string>
<string>am</string>
<string>the</string>
<string>best</string>
<string>one</string>
<string>.</string>
</array>
</plist>
*/
//3.将字典数据写入文件
//1.定义一个字典的文件存储路径
NSString *dicPath = [documentsPath stringByAppendingPathComponent:@"dic.plist"];
//2.创建字典对象
NSDictionary *dic = @{@"name" : @"laomou", @"age" : @28, @"height" : @"178"};
//3.调用字典中定义的实例方法writeToFile:atomically:将字典的内容写入到对应路径文件中
[dic writeToFile:dicPath atomically:YES];
//4. NSData二进制字节流 - 二进制对象写入文件
//1.
UIImage *image = [UIImageimageNamed:@"Highest.jpg"];
//2.image无法直接写入文件,可以先转换成二进制
NSData *imageData= UIImageJPEGRepresentation(image, 0.5);//第二个参数:CGFloatcompressionQuality - 压缩质量, 范围在0~1之间, 1代表是100%不压缩
//3.创建 - 该方法可以用来压缩上传的头像
NSString *imagePath = [documentsPath stringByAppendingPathComponent:@"image.jpg"];
//4.将二进制字节流对象写入到文件中
[imageData writeToFile:imagePath atomically:YES];
写入的文件如下图:
三、复杂对象写入文件(复杂对象(框架中不存在的类 - 自定义的对象))
//对于自定义类的实例对象,需要通过归档和反归档来进行持久化保存 -这并不是最优化的方式,优选的是使用数据库
//1.对应类遵守NSCoding协议
//2.实现相关的编码(-(void)encodeWithCoder:(NSCoder*)aCoder)和解码(-(id)initWithCoder:(NSCoder *)aDecoder)协议
//3.使用NSKeyedArchiver来进行归档保存(调用编码协议)
//4.使用NSKeyedUnarchiver来进行反归档(调用解码协议)
举例:
Student *stu1 = [StudentstudentWithName:@"zhangsan"age:25 sex:@"male"];
Student *stu2= [Student studentWithName:@"lisi" age:24 sex:@"女"];
Student *stu3= [Student studentWithName:@"wangwu" age:24 sex:@"男"];
//NSCoding - 编码和解码协议 必须实现NSCoding协议,并且要实现协议方法
//编码(encode)的时候key可以随便写, 但是解码(decode)的时候必须和编码的时候的key对应 - 规范的写法就是提前把key定义成宏,用的时候用宏就可以了
1.在Student.h中 让要归档的Student遵循NSCoding协议
@interfaceStudent: NSObject <NSCoding>//如果一个自定义类的实例对象需要被持久化保存在沙盒文件中,则该类需要遵循编码协议,并实现编码协议中的编码和解码方法
2.在Student.m中实现协议方法(该协议中共有以下两个协议方法)
//实现编码协议,让当前类在归档的时候对对应的属性值做编码
- (void)encodeWithCoder:(NSCoder*)aCoder {
NSLog(@"%s",__func__);
[aCoder encodeObject:self.name forKey:kStudentNameKey];
[aCoder encodeObject:self.sex forKey:kStudentSexKey];
[aCoder encodeInteger:self.age forKey:kStudentAgeKey];
}
//实现解码协议,当此类的对象被反归档时,首先通过解码协议方法来获取对应的属性值
- (id)initWithCoder:(NSCoder*)aDecoder {
self = [super init];
if (self) {
self.name= [aDecoder decodeObjectForKey:kStudentNameKey];
self.age= [aDecoder decodeIntegerForKey:kStudentAgeKey];
self.sex= [aDecoder decodeObjectForKey:kStudentSexKey];
NSLog(@"%s",__func__);
}
return self;
}
3.使用NSKeyedArchiver来进行归档保存
//如果要将自定义类的对象写入文件中,则需要使用归档
[NSKeyedArchiver archiveRootObject:studentstoFile:stusPath];
4.使用NSKeyedUnarchiver来进行反归档
//如果想要读取已经归档的文件,则需要反归档
NSArray *unArcStus = [NSKeyedUnarchiverunarchiveObjectWithFile:stusPath];
for (Student*stu in unArcStus) {
NSLog(@"%@%lu, %@",stu.name,stu.age,stu.sex);
}