@qidiandasheng
2016-09-05T12:07:53.000000Z
字数 6484
阅读 3096
iOS理论
所谓数据持久化,就是数据的存储。在iOS里数据的存储有以下几种:
在说数据存储之前肯定绕不开苹果的沙盒机制。出于安全考虑,iOS系统的沙盒机制规定每个应用都只能访问当前沙盒目录下面的文件(也有例外,比如系统通讯录能在用户授权的情况下被第三方应用访问)。
Xcode7上模拟器的沙盒路径:
数据目录:
/Users/用户名/Library/Developer/CoreSimulator/Devices/模拟器UDID/data/Containers/Data/Application/对应应用程序文件夹
这里面主要有Documents、Library(Caches和Preferences)、tmp,如图:

.app目录:
/Users/用户名/Library/Developer/CoreSimulator/Devices/模拟器UDID/data/Containers/Bundle/Application/对应应用程序文件夹/应用名.app
获取沙盒目录:
NSLog(@"%@",NSHomeDirectory());输出:/var/mobile/Applications/21A34B34-9B30-4B8A-854E-1553480C078F
存放内容:
该目录包含了应用程序本身的数据,包括资源文件和可执行文件等。程序启动以后,会根据需要从该目录中动态加载代码或资源到内存,这里用到了lazy loading的思想。
整个目录是只读的:
为了防止被篡改,应用在安装的时候会将该目录签名。非越狱情况下,该目录中内容是无法更改的;在越狱设备上如果更改了目录内容,对应的签名就会被改变,这种情况下苹果官网描述的后果是应用程序将无法启动
是否会被iTunes同步
否
获取目录
NSLog(@"%@",[[NSBundle mainBundle] bundlePath]);输出:/var/mobile/Containers/Data/Applications/21A34B34-9B30-4B8A-854E-1553480C078F/PhoneCall.app
存放内容
我们可以将应用程序的数据文件保存在该目录下。不过这些数据类型仅限于不可再生的数据,可再生的数据文件应该存放在Library/Cache目录下。
是否会被iTunes同步
是
获取目录
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);NSString *path = [paths objectAtIndex:0];NSLog(@"%@",path);输出:/var/mobile/Containers/Data/Applications/21A34B34-9B30-4B8A-854E-1553480C078F/Documents
存放内容
苹果建议用来存放默认设置或其它状态信息。
是否会被iTunes同步
是,但是要除了Caches子目录外
获取目录
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);NSString *path = [paths objectAtIndex:0];NSLog(@"%@", path);输出:/var/mobile/Containers/Data/Application/21A34B34-9B30-4B8A-854E-1553480C078F/Library
存放内容
主要是缓存文件,用户使用过程中缓存都可以保存在这个目录中。前面说过,Documents目录用于保存不可再生的文件,那么这个目录就用于保存那些可再生的文件,比如网络请求的数据。鉴于此,应用程序通常还需要负责删除这些文件。
SDWebImage的图片缓存就是存储在这里。
是否会被iTunes同步
否
获取目录
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);NSString *path = [paths objectAtIndex:0];NSLog(@"%@", path);输出:/var/mobile/Containers/Data/Application/21A34B34-9B30-4B8A-854E-1553480C078F/Library/Caches
存放内容
应用程序的偏好设置文件。我们使用NSUserDefaults写的设置数据都会保存到该目录下的一个plist文件中,这就是所谓的写到plist中。
是否会被iTunes同步
是
存放内容
各种临时文件,保存应用再次启动时不需要的文件。而且,当应用不再需要这些文件时应该主动将其删除,因为该目录下的东西随时有可能被系统清理掉,目前已知的一种可能清理的原因是系统磁盘存储空间不足的时候。
是否会被iTunes同步
否
获取目录
NSLog(@"%@",NSTemporaryDirectory());输出:/private/var/mobile/Containers/Data/Applications/21A34B34-9B30-4B8A-854E-1553480C078F/tmp/
plist,全名PropertyList,即属性列表文件,它是一种用来存储串行化后的对象的文件。这种文件,在ios开发过程中经常被用到。这种属性列表文件的扩展名为.plist,因此通常被叫做plist文件。文件是xml格式的。Plist文件是以key-value的形式来存储数据。既可以用来存储用户设置,也可以用来存储一些需要经常用到而不经常改动的信息。
可以被序列化的类型只有如下几种:
NSArray;NSMutableArray;NSDictionary;NSMutableDictionary;NSData;NSMutableData;NSString;NSMutableString;NSNumber;NSDate;
获得文件路径:
我们看到一般plist文件都是存储在沙盒的Documents目录下的。如果document里没有此文件,会自动创建,读取赋值后,便可使用。
NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;NSString *fileName = [path stringByAppendingPathComponent:@"userName.plist"];
存储
NSArray *array = @[@"齐滇大圣", @"哈哈", @"呵呵"];[array writeToFile:fileName atomically:YES];
读取
NSArray *result = [NSArray arrayWithContentsOfFile:fileName];NSLog(@"%@", result);
删除plist文件
NSFileManager *fileManage = [NSFileManager defaultManager];NSString *filePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)objectAtIndex:0]stringByAppendingPathComponent:@"userName.plist"];//如果文件路径存在的话BOOL isExist = [fileManage fileExistsAtPath:filePath];if (isExist) {NSError *err;[fileManage removeItemAtPath:filePath error:&err];}
偏好设置主要用来存储一些存储轻量级的本地数据,比如一些应用程序的配置信息,用户名、密码之类的。
偏好设置会将所有数据保存到同一个文件中。即preference目录下的一个以此应用包名来命名的plist文件。
使用
//获得NSUserDefaults文件NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];//向文件中写入内容[userDefaults setObject:@"齐滇大圣" forKey:@"name"];[userDefaults setBool:YES forKey:@"sex"];[userDefaults setInteger:21 forKey:@"age"];//立即同步,如果没有调用synchronize方法,系统会根据I/O情况不定时刻地保存到文件中。所以如果需要立即写入文件的就必须调用synchronize方法。[userDefaults synchronize];//读取文件NSString *name = [userDefaults objectForKey:@"name"];BOOL sex = [userDefaults boolForKey:@"sex"];NSInteger age = [userDefaults integerForKey:@"age"];NSLog(@"%@, %d, %ld", name, sex, age);
DSCategories里的NSUserDefaults+SafeAccess分类可以快速方便的操作NSUserDefaults。
归档在iOS中是另一种形式的序列化,只要遵循了NSCoding协议的对象都可以通过它实现序列化。NSString、NSDictionary、NSArray、NSData、NSNumber等类型都遵循了NSCoding协议。
NSCoding协议有2个方法:
- (void)encodeWithCoder:(NSCoder *)aCoder
每次归档对象时,都会调用这个方法。一般在这个方法里面指定如何归档对象中的每个实例变量,可以使用encodeObject:forKey:方法归档实例变量
- (id)initWithCoder:(NSCoder *)aDecoder
每次从文件中恢复(解码)对象时,都会调用这个方法。一般在这个方法里面指定如何解码文件中的数据为对象的实例变量,可以使用decodeObject:forKey方法解码实例变量
归档一个NSArray对象
NSString *filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"array.archive"];NSArray *array = [NSArray arrayWithObjects:@”哈哈”,@”呵呵”,nil];[NSKeyedArchiver archiveRootObject:array toFile:filePath];//恢复NSArray对象NSArray *array = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
归档Person对象
@interface Person : NSObject<NSCoding>@property (nonatomic, copy) NSString *name;@property (nonatomic, assign) int age;@property (nonatomic, assign) float height;@end
@implementation Person- (void)encodeWithCoder:(NSCoder *)encoder {[encoder encodeObject:self.name forKey:@"name"];[encoder encodeInt:self.age forKey:@"age"];[encoder encodeFloat:self.height forKey:@"height"];}- (id)initWithCoder:(NSCoder *)decoder {self.name = [decoder decodeObjectForKey:@"name"];self.age = [decoder decodeIntForKey:@"age"];self.height = [decoder decodeFloatForKey:@"height"];return self;}@end
NSString *filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.archive"];Person *person = [[Person alloc] init];person.name = @"hosea";person.age = 22;person.height = 1.83f;[NSKeyedArchiver archiveRootObject:person toFile:filePath];
Person *person = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
注意
如果父类也遵守了NSCoding协议,请注意:
应该在encodeWithCoder:方法中加上一句[super encodeWithCode:encode];确保继承的实例变量也能被编码,即也能被归档应该在initWithCoder:方法中加上一句self = [super initWithCoder:decoder];确保继承的实例变量也能被解码,即也能被恢复
SQLite3是一款开源的嵌入式关系型数据库,可移植性好、易使用、内存开销小。
SQLite3是无类型的,意味着你可以保存任何类型的数据到任意表的任意字段中。
一般我们创建的的数据库是生成在Doucuments目录下以dataName.sqlite命名的。
iOS中原生的SQLite API在使用上相当不友好,于是我们一般都会使用将SQLite API进行过封装的第三方库。比如FMDB。
Core Data 是 iOS3.0 时引入的一个数据持久化的框架。他与 sqlite 对比最大的优点莫过于支持对象的存储了,苹果的官方文档说其简化了数据库的操作,使用 CoreData 确实可以大量减少代码中的 SQL 语句。
其实 Core Data 是构建在 SQLite 之上,对数据存储层进行了进一步的抽象。虽然说Core Data减少了SQL语句,但是其学习成本并没有降低,复杂度依旧挺高,而且性能也没特别好。
详情可以阅读唐巧的我为什么不喜欢 Core Data这篇文章。