iOS应用数据存储的常用方式
- XML属性列表(plist) 归档
- Preference(偏好设置)
- NSKeyedArchiver归档(NSCoding)
- SQLine3
- Core Data
本篇博客只介绍前三种存储的常用方式.后两种后续会继续更新.
在讲述iOS数据存储之前.首先要了解什么是应用沙盒,以及沙盒中的目录结构.
应用沙盒
- 每个iOS应用都有自己的应用沙盒(应用沙盒就是文件系统目录). 与其他文件系统隔离.应用必须待在自己的沙盒中,其他应用不可以访问该沙盒
- 应用沙盒的文件系统目录.如下
注意:
我们可以通过NSLog(@"%@",NSHomeDirectory)来打印 应用程序沙盒的根目录
接下来针对以上目录,一一说明其有什么作用:
- Documents: 保存应用运行时生成的需要持久化数据,iTunes同步设备时会备份该目录.
注意: 在Documents目录中不可以存放一些网络中下载的比较大的数据,因为iTunes会备份,如果存放一些比较大的数据,当应用程序打包上传appStore 会被审核不通过.
Documents目录 一般存放一些应用程序生成的一些数据
- tmp: 保存应用运行时所需的临时数据,使用完毕后再将相应的文件从该目录删除.应用程序没有运行时,系统可能会清除该目录下的文件.iTunes同步设备时不会备份该目录
- Library/Caches: 保存应用运行时生成的需要持久化的数据,iTunes同步设备不会备份该目录.一般存储体积大、不需要备份的非重要的数据
Library/Preference: 保存应用的所有偏好设置.iOS的应用会在该目录中查找应用的设置信息。iTunes同步设备时会备份该目录
应用沙盒目录的获取方式.
Plist存储:
plist 件存储 般都是存取字典和数组,直接写成plist 件,把它存到应 沙盒当
中.只有在ios当中才有plist存储,它是ios特有的存储 式.
介绍完沙盒中的目录后.来讲解一下如何获取沙盒中的目录.
- 沙盒的根目录: NSString *path = NSHomeDirectory();
在iOS中,只有一个目录跟传入的参数匹配,所以这个集合里面只有一个元素
这个方法的参数:
arg1: NSDocumentDirectory 找到沙盒中目录(NSDocument,Caches)
arg2: NSUserDomainMask 搜索的范围(一般写NSUserDomainMask,沙盒的固定范围)
arg3: 是否展开路径(YES) 注: 在iOS开发中不识别~,在Mac平台可识别,所以必须要展开路径
NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
在Documents目录下拼接一个文件名:
NSString *filePath = [path stringByAppendingPathComponent:@"data.plist"];
把字典数据保存到沙盒目录下的data.plist文件中
NSDictionary *dictArray = @{@"name" : @"ZY"};
[dictArray writeToFile: filePath atomically: YES];
在这里.可以打印一下path,进入到沙盒的Documents目录下的data.plist文件中.数据是否已经保存在里面.
从沙盒目录下的文件中读取数据:
需要使用到dictionaryWithContentsOfFile:方法
// 读取沙盒目录中的字典文件
NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
// 拿到字典文件名称
NSString *filePath = [path stringByAppendingPathComponent:@"data.plist"];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:filePath];
NSLog(@"%@",dict);
属性列表:
- 属性列表是一种XML格式的文件,拓展名为plist
- 如果对象是NSString、NSDictionary、NSArray、NSData、NSNumber等类型,就可以使用writeToFile:atomically:方法直接将对象写到属性列表文件中
- 许多iOS应用都支持偏好设置,比如保存用户名,密码,字体大小等设置,iOS提供了一套标准的解决方案来为应用加入偏好设置功能.
- 每个应用都有NSUserDefaults实例,通过它来存储偏好设置
- 比如 保存用户名,字体大小,是否自动登录
- 注意: NSUserDefaults 保存的也是一个plist文件
保存数据到沙盒中
// Preferences 保存在library/Preferences目录中(plist文件)
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:@"ZY" forKey:@"name"];
[defaults setInteger:19 forKey:@"age"];
// 立刻写入到文件中(同步操作)
[defaults synchronize];
NSLog(@"%@",NSHomeDirectory());
从沙盒中读取数据
// 读取
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// 根据key去取值
NSString *name = [defaults objectForKey:@"name"];
NSLog(@"%@",name);
NSInteger integer = [defaults integerForKey:@"age"];
NSLog(@"%ld",integer);
归档:
归档一般都是保存自定义对象的时候,使用归档.因为plist文件不能保存自定义对象.
如果一个字典当中保存有自定义对象,如果把这个字典写入到文件中,不会生产plist文件.
写入到沙盒的tmp目录下
archiveRootObject会调用encodeWithCode: 方法
Person *person = [[Person alloc] init];
person.name = @"ZY";
person.age = 19;
Dog *dog = [[Dog alloc] init];
dog.name = @"wangcai";
person.dog = dog;
// 获取沙盒中的指定目录(tmp目录)
NSString *path = NSTemporaryDirectory();
NSLog(@"%@",path);
// 拼接文件名称
NSString *filePath = [path stringByAppendingPathComponent:@"person.data"];
// 归档(会调用encodeWithCoder方法) 把对象保存到沙盒的指定目录中
[NSKeyedArchiver archiveRootObject:person toFile:filePath];
unarchiveObjectWithFile会调用 initWithCoder 方法
// 接档操作
NSString *path = NSTemporaryDirectory();
NSString *filePath = [path stringByAppendingPathComponent:@"person.data"];
// 解档(什么类型,会调用initWithCoder: 方法)
Person *per = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
NSLog(@"%@--%@",per.name,per.dog.name);
Person文件
#import
@class Dog;
@interface Person : NSObject
/** 姓名 */
@property(nonatomic,strong)NSString * name;
/** 年龄 */
@property(nonatomic,assign)int age;
/** 狗 */
@property(nonatomic,strong)Dog * dog;
@end
@implementation Person
// 保存当前对象的哪些属性
//- (void)encodeWithCoder:(NSCoder *)aCoder
//{
// [aCoder encodeObject:self.name forKey:@"name"];
// [aCoder encodeInt:self.age forKey:@"age"];
//}
//
当解析一个文件的时候调用.(告诉当前要解析文件当中哪些属性)
//- (instancetype)initWithCoder:(NSCoder *)aDecoder
//{
// if (self = [super init]) {
// self.name = [aDecoder decodeObjectForKey:@"name"];
// self.age = [aDecoder decodeIntForKey:@"age"];
// }
//
// return self;
//}
// 保存当前对象的哪些属性到沙盒中
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.name forKey:@"name"];
[aCoder encodeInt:self.age forKey:@"age"];
[aCoder encodeObject:self.dog forKey:@"dog"];
}
// 当解析一个文件的时候调用(告诉当前要解析文件当中的哪些属性)
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
// 如果父类也准守了协议,就可以调用initWithCoder:方法
if (self = [super init]) {
// 需要解析文件中哪些属性
self.name = [aDecoder decodeObjectForKey:@"name"];
self.age = [aDecoder decodeIntForKey:@"age"];
self.dog = [aDecoder decodeObjectForKey:@"dog"];
}
return self;
}
@end
NSKeyedArchiver-归档对象的注意
- 如果父类也遵守了NSCoding协议,请注意:
- 应该在encodeWithCoder:方法中加上一句
[super encodeWithCode:encode];
确保继承的实例变量也能被编码,即也能被归档
- 应该在initWithCoder:方法中加上一句
self = [super initWithCoder:decoder];
确保继承的实例变量也能被解码,即也能被恢复