一、数据持久化概述
数据持久化就是数据的永久存储。其本质是将数据保存为文件,存到程序的沙盒中。
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 = [homePathstringByAppendingPathComponent:@"library/caches"];
NSLog(@"%@", documentPath2);
//2.获取应用程序包路径(.app)
NSLog(@"%@", [NSBundlemainBundle].resourcePath);
二、简单对象持久化
1、简单对象
NSString\NSArray\NSDictionary\NSData
使用writeToFile:方法,将数据存储为.plist文件
atomically参数为是否写入缓存
//字符串
NSString*string =@"I?U";
//数组
NSArray*array =@[@"张三",@"李四",@"王五"];
//字典
NSDictionary*dictionary =@{@"name":@"张三",@"age":@"20",@"sex":@"男"};
//NSData
UIImage*image = [UIImageimageNamed:@"1.jpg"];
NSData*data =http://my.oschina.net/zooyf/blog/UIImageJPEGRepresentation(image,1);
//1.拼接存储路径
NSString*strPath = [documentPathstringByAppendingPathComponent:@"string.txt"];
NSString*arrayPath = [documentPathstringByAppendingPathComponent:@"array.txt"];
NSString*dicPath = [documentPathstringByAppendingPathComponent:@"dict.txt"];
NSString*dataPath = [documentPathstringByAppendingPathComponent:@"data.txt"];
//2.写入文件
[stringwriteToFile:strPathatomically:YESencoding:NSUTF8StringEncodingerror:nil];
[arraywriteToFile:arrayPathatomically:YES];
[dictionarywriteToFile:dicPathatomically:YES];
[datawriteToFile:dataPathatomically:YES];
//3.读取文件内容
NSString*fileString = [NSStringstringWithContentsOfFile:strPathencoding:NSUTF8StringEncodingerror:nil];
NSArray*fileArray = [NSArrayarrayWithContentsOfFile:arrayPath];
NSDictionary*fileDict = [NSDictionarydictionaryWithContentsOfFile:dicPath];
NSData*fileData = http://my.oschina.net/zooyf/blog/[NSDatadataWithContentsOfFile: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 = [cachesPathstringByAppendingPathComponent:@"path"];
NSFileManager*fileManager = [NSFileManagerdefaultManager];//创建文件管理类单例对象
//根据路径创建文件夹
NSDictionary*fileDate =@{@"createTime":@"2015-9-9"};
[fileManagercreateDirectoryAtPath:directoryPathwithIntermediateDirectories:YESattributes:fileDateerror:nil];
//根据路径创建文件(只能写入NSData类型的数据)
[fileManagercreateFileAtPath:directoryPathcontents:dataattributes:fileDate];
//删除文件
[fileManagerremoveItemAtPath:dicPatherror:nil];//删除~/documents/dict.txt
3、NSUserDefaults
NSUserDefaults*defaults = [NSUserDefaultsstandardUserDefaults];//单例
[defaultssetValue:@"yfyfyfyfyfyfyfy"forKey:@"username"];
[defaultssetValue:@"123"forKey:@"password"];
//注意:UserDefaults设置数据时,不是立即写入,而是根据时间戳定时地把缓存中的数据写入本地磁盘。所以调用了set方法之后数据有可能还没有写入磁盘应用程序就终止了。出现以上问题,可以通过调用synchornize方法强制写入到文件中
[defaultssynchronize];
//读取
NSString*name = [defaultsvalueForKey:@"username"];
NSString*pwd = [defaultsvalueForKey:@"password"];
二、复杂对象持久化(NSKeyedArchiver)
1、复杂对象
复杂对象是在Foundation框架内不存在的数据类,无法通过writeToFile写入到文件内,且至少包含一个实例对象。
由于复杂对象无法通过writeToFile:方法写入文件,只能将复杂对象转化为NSData对象,再进行数据持久化。
2、NSCoding协议
@protocolNSCoding
- (void)encodeWithCoder:(NSCoder*)aCoder;
- (id)initWithCoder:(NSCoder*)aDecoder;// NS_DESIGNATED_INITIALIZER
@end
3、复杂对象写入文件
Person.h
//复杂对象归档一:遵守NSCoding协议
@interfacePerson :NSObject
@property(nonatomic,assign)NSIntegerage;
@property(nonatomic,retain)NSString*name;
@property(nonatomic,retain)NSString*gender;
@end
Person.m
#import"Person.h"
@implementationPerson//实现NSCoding协议
#pragma mark --进行编码--
- (void)encodeWithCoder:(NSCoder*)coder
{
// [super encodeWithCode:coder];如果父类也遵守了NSCoding协议,确保继承的实例变量也能被编码,即也能被归档
[coderencodeObject:self.nameforKey:@"name"];
[coderencodeInteger:self.ageforKey:@"age"];
[coderencodeObject:self.genderforKey:@"gender"];
}
#pragma mark --进行解码--
- (id)initWithCoder:(NSCoder*)aDecoder
{
// self = [super initWithCoder:aDecoder];确保继承的实例变量也能被解码,即也能被恢复
self= [superinit];
if(self) {
self.name= [aDecoderdecodeObjectForKey:@"name"];
self.gender= [aDecoderdecodeObjectForKey:@"gender"];
self.age= [aDecoderdecodeIntegerForKey:@"age"];
}
returnself;
}
@end
ViewController.m
类方法进行编码\解码(只能归档一个对象):
NSString*objPath = [cachesPathstringByAppendingPathComponent:@"person.txt"];
[NSKeyedArchiverarchiveRootObject:persontoFile:objPath];
Person*p2 = [NSKeyedUnarchiverunarchiveObjectWithFile:objPath];
实例方法(可以归档多个对象):
#pragma mark --对复杂对象进行持久化(归档\编码) --
//过程:(复杂对象->归档->NSData->writeToFile:)
Person*person = [[Personalloc]init];
person.name=@"yf";
person.age=20;
person.gender=@"man";
NSMutableData*mtData = http://my.oschina.net/zooyf/blog/[NSMutableDatadata];
//创建归档器
NSKeyedArchiver*archiver = [[NSKeyedArchiveralloc]initForWritingWithMutableData:mtData];
//进行归档
[archiverencodeObject:personforKey:@"person"];
//***结束归档
[archiverfinishEncoding];
//将归档之后的mtData写入文件
NSString*personPath = [cachesPathstringByAppendingPathComponent:@"person.txt"];
[mtDatawriteToFile:personPathatomically:YES];
NSLog(@"%@", personPath);
NSLog(@"%@", mtData);
#pragma mark --从文件中读取复杂对象(反归档\恢复\解码) --//过程:(读取文件(NSData)->反归档->复杂对象)
//读取
NSData*readData = http://my.oschina.net/zooyf/blog/[NSDatadataWithContentsOfFile:personPath];
//创建反归档工具
NSKeyedUnarchiver*unArchiver = [[NSKeyedUnarchiveralloc]initForReadingWithData:readData];
//使用反归档工具对readData进行反归档
Person*readPerson = [unArchiverdecodeObjectForKey:@"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
在IOS应用中数据持久化是客户端重要的东西。一般除了本地持久化就是与服务器交互数据从网上获取了,但是如果没有网络的情况下。本地持久化是必须的。一般持久化有4种方法。现在一一介绍,对于网络的就以后介绍。
第一种:实用属性列表,第二种:对象归档,第三种:使用Iphone的嵌入式数据库(SQLite3)
给予Iphone应用程序沙盒原理,我们保持的数据都是保存在相对应的应用程序的Document文件夹。既然我们把数据放在每一个应用的Document文件夹中,呢我我们怎么得到相应的路径呢,其实也不是很难。下面是检索文档目录路径的代码:
NSArray*paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserdomainMask, YES);
NSString*documentDirectory = [paths objectAtIndex:0];
常量NSDocumentDirectory表面我们正在查找的Document目录路径,常量NSUserDomainmask表明我们希望将搜索限制于我们应用程序的沙盒中。这样我们就可以得到该数组的第一值,也仅此一值,因为每一个应用程序只有一个Document文件夹。我们得到了Document的路径,然后和文件名相连接不就是一个完整的路径了吗,这用到了stringByAppendingPathComponent方法:
NSString*filepath = [documentDirectorystringbyAppendingPathComponent:@"filename.xxx"];其中filename.xxx为要命名的文件。
在每一个应用程序中还对应一个temp文件夹,我们怎么获取这个文件夹的路径呢,也比较简单:
NSString*tempPath = NSTemporaryDirectory();
NSString*filePath = [tempPathstringByAppendingPathComponent:@"filename.xxx"];
NSUserDefaults类的使用和NSKeyedArchiver有很多类似之处,但是查看NSUserDefaults的定义可以看出,NSUserDefaults直接继承自NSObject而NSKeyedArchiver 继承自NSCoder。这意味着NSKeyedArchiver实际上是个归档持久化的类,也就可以使用NSCoder类的[encodeObject: (id)objv forKey:(NSString *)key]方法来对数据进行持久化存储
除了上面的三种方法来保存持久化 数据以外,我们还可以用写文件到磁盘的方式来保存持久化数据
嵌入式数据库持久化数据就是把数据保存在iphone的嵌入式数据库系统SQLite3中,本质上来说,数据库持久化操作是基于文件持久化基础之上的。 要使用嵌入式数据库SQLite3,首先需要加载其动态库libsqlite3.dylib,这个文件位于/Xcode3.1.4/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.1.sdk/usr/lib目录下
Core Data是一种 稳定,功能全面的持久化工具,和之前的一些持久化 工具相比,他不需要对实体进行归档,也就是序列化,而是在数据 模型编辑器中创建一些实体
在代码中,你不再使用存取方法和修改方法,而是使用键值对编码来设置属性或者减缩他们的值
那么这些托管对象的活动区域在哪 ? 他们位于所谓的持久库中,默认情况下,Core Data应用程序将持久库实现为存储在应用程序文档目录的sqlite数据库。
虽然数据是通过sqlite存储的,但框架中的类将完成加载和保存数据的相关工作。不许要编写任何sql语句。
其实在Iphone上存储数据是在iPhone系统中存储数据的,这里可以包括应用程序和互联网的一些数据信息。我们可以解析和读取这些数据。首先我们先了解下数据持久化的几种方式:首先是属性列表。nsuserdefaults位于属性列表的最顶层,我们可以在里面添加一些UI的小单元
还有就是iPhone的应用程序沙盒技术,以及对象的归档,以及第三方的SQLlite,还有就是coredata。
首先让我们说说这个属性列表:它是存储相对较小数据量的简洁方式,并且耗费非常低的工作量。但是这个属性列表也是有限制的像刚才我说的存储数据量小的数据,还有就是一些自定义的对象是不能存储的,只能是支持序列化的对象,想字典,数据,字符串,对了 还是不能添加UI界面的东西。他们可以以两种不同的方式存储一个是XML,这个XML是一种可读性非常强的格式,另一个是内部专门有的2进制的格式,这种方式速度更快写,数据量更小些。正如我们所说的NSuerdefaults位于属性列表的顶层,因此我们可以轻松的读取并且存储数据。
下面我们说说有几种情况来说明有些数据是不能存储的:比如你有大量的几百兆的数据这样是不行的,还有是一堆的图像,你将不会选择这个方式,因为你任何时候加载一个属性列表的时候他们是完全在家在内存当中的,知道一个完整的图片被创建,因此这种方式是不适用随机访问的。假如有两个线程一个是写入一个是读取这是不能同步的,同步会出现一些问题的,这是为什么呢,因为属性列表没有类似有原子性的东西。我们会有两种方式把他们写在硬盘内writetofile:@"文件路径"atomically:yes。这个原子性是什么意思呢,在这里可以这样的理解,它可以让系统写入文件一次首先是把它写入到一个临时的文件后然后在将其转移到可你以放置任何文件的没有风险的地方这里将保存着先前的文件以及任何改动的地方,因此如果操作 系统崩溃或者电池电量的耗尽的时候你也不会丢失任何的数据的。你可以从你先前的或者新的数据里获取数据而非受破坏的中间的状态。当你在写一个属性列表的时候如果你看XML和2进制。自定义和一些UI的视图不支持这些东西取决与可不可以序列化对象,当然不可以序列化,即使你序列化这些不能序列化的东西它会返回空值的。