最近在学习在iOS开发中如何做数据的持久化。我们在开发APP时有时需要将数据保存到本地。下面总结一下几种数据持久化的方式及其应用。这只是我的学习笔记,有错误的地方还望指点。
数据持久化的几种方式:
a.XML属性列表(plist)
b.Preference(偏好设置)
c.NSKeyedArchiver归档(NSCoding)
d.SQLite
e.Core Data
这篇笔记先总结一下前三种(plist、偏好设置、归档)。后面两种(SQLite、Core Data)在下篇总结。在介绍之前先了解一下沙盒(sandbox)结构。
我们通过如下代码:
NSString *homePath = NSHomeDirectory();
NSLog(@"%@",homePath);
可以获取到沙盒路径。
然后将路径贴到Finder前往文件夹中就可以看到沙盒中如下结构
Documents:保存应用运行时生成的需要持久化的数据,iTunes同步设备时会备份该目录。例如,游戏应用可将游戏存档保存在该目录,还有我们的通讯录之类的。
Library/Caches:保存应用运行时生成的需要持久化的数据,iTunes同步设备时不会备份该目录。一般存储体积大、不需要备份的非重要数据。比如我们朋友圈缓存的照片。
Library/Preference:保存应用的所有偏好设置,iOS的Settings(设置)应用会在该目录中查找应用的设置信息。iTunes同步设备时会备份该目录。比如我们设置的字体、皮肤、账户信息等。
tmp:保存应用运行时所需的临时数据,使用完毕后再将相应的文件从该目录删除。应用没有运行时,系统也可能会清除该目录下的文件。iTunes同步设备时不会备份该目录。
1.路径获取
(1) Documents
方式1:
documenstPath = [homePath stringByAppendingPathComponent:@"Documents"];
方式2:
documenstPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
(2) 获取Caches路径
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
//参数:YES:全路径 NO:相对路径
注:Preference路径不能用搜索的方式去获取,可以通过拼接路径获取到,但是苹果不希望我们这样做。
要使用偏好设置,是这样获取得的
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
(3) 获取tmp路径
NSString *tmpPath = NSTemporaryDirectory();
2.plist的使用
plist文件保存数组
// 1.创建数组
NSArray *array = @[@"1",@"2",@"3"];
// 2.获取Documents路径 沙盒/Documents
NSString *documents = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
// 3.拼接文件路径 沙盒/Documents/array.plist
NSString *filePath = [documents stringByAppendingPathComponent:@"array.plist"];
// 4.写入文件
BOOL success = [array writeToFile:filePath atomically:YES];
//atomically:线性安全的
读取保存好的数组
// 1.获取documents路径
NSString *documents = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
//2.拼接文件路径
NSString *filePath = [documents stringByAppendingPathComponent:@"array.plist"];
// 2.读取数组
NSArray *array = [NSArray arrayWithContentsOfFile:filePath];
plist文件保存字典
// 1.创建字典
NSDictionary *dict = @{@"a":@"1",@"b":@"2",@"c":@"3"};
// 2.获取Documents路径 沙盒/Documents
NSString *documents = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
// 3.拼接文件路径 沙盒/Documents/dict.plist
NSString *filePath = [documents stringByAppendingPathComponent:@"dict.plist"];
// 4.写入文件
[dict writeToFile:filePath atomically:YES];
读取保存好的字典
// 1.获取documents路径
NSString *documents = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
//2.拼接文件路径
NSString *filePath = [documents stringByAppendingPathComponent:@"dict.plist"];
// 3.读取字典
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:filePath];
2.Preference(偏好设置)
// 1.获取偏好设置 default/standard/share/main
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// 2.保存数据
[defaults setObject:@"仰望星空" forKey:@"name"];
[defaults setBool:YES forKey:@"boy"];
// 3.读取数据
NSString *name = [defaults objectForKey:@"name"];
BOOL sex = [defaults boolForKey:@"boy"];
NSLog(@"%@ %@",name,sex?@"YES":@"NO");
// iOS8之前 需要同步
[defaults synchronize];
Preference(偏好设置) 是通过 set 。。。forKey。。。来保存数据的
一般有以下几种方式
读取也是通过相应的方法来读取
[defaults objectForKey:@"name"];
[defaults boolForKey:@"boy"];
3.NSKeyedArchiver归档(NSCoding)
在iOS开发中,经常需要保持一些对象,属性列表和偏好设置均不能实现。针对这种情况,iOS提供了对象归档技术。它采用序列化的方式,实现对象的存储。通常来说对象归档的操作有两个方面。具体如下:
对象归档:以一种不可读的方式,将对象写入到指定文件中。
对象反归档:从指定文件中读取数据,并自动的重建对象。
对于这两种情况,iOS提供了相应的类,实现对象的归档和反归档,具体如下:
1.NSKeyedArchiver 类
NSKeyedArchiver 类直接继承NSCoder类,将对象归档到指定的文件中,该类提供了两个方法。
+ (NSData *)archivedDataWithRootObject:(id)rootObject;
+ (BOOL)archiveRootObject:(id)rootObject toFile:(NSString *)path;
均是类方法无需创建对象,直接使用类名NSKeyedArchiver 调用就可以了。其中 archiveRootObject 方法需要传入一个路径参数,该参数用于指定对象保存路径。
2.NSKeyedUnarchiver 类
NSKeyedUnarchiver 类也是直接继承NSCoder类,负责从文件中回复对象,该类提供了两个方法,具体格式如下:
+ (nullable id)unarchiveObjectWithData:(NSData *)data;
+ (nullable id)unarchiveObjectWithFile:(NSString *)path;
unarchiveObjectWithFile 方法也是需要传入一个路径参数。
NSCoding协议
在对象的归档技术中,如果对象要归档需要遵守NSCoding协议,凡是遵守了NSCoding协议的自定义对象,都可以实现对象的归档和反归档。NSCoding协议中定义了两个方法,这两个方法是对象归档必须实现的。
- (void)encodeWithCoder:(NSCoder *)aCoder; //该方法负责归档该对象的所有是咧变量
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder; //该方法负责恢复该对象的实例变量的值
所以通过重写这两个方法,来指定如何归档和回复对象的每个实例变量,为此,NSCoder类提供了相应的方法来实现对象的归档、恢复对象每个实例变量的方法。
常用的归档数据的方法
- (void)encodeObject:(nullable id)objv forKey:(NSString *)key; //将Object类型编码,使其与字符串类型的键相关联
- (void)encodeBool:(BOOL)boolv forKey:(NSString *)key; //将BOOL类型编码,使其与字符串类型的键相关联
- (void)encodeInt:(int)intv forKey:(NSString *)key; //将int类型编码,使其与字符串类型的键相关联
- (void)encodeFloat:(float)realv forKey:(NSString *)key; //将float类型编码,使其与字符串类型的键相关联
- (void)encodeDouble:(double)realv forKey:(NSString *)key; //将double类型编码,使其与字符串类型的键相关联
常用的恢复对象实例变量的方法
- (nullable id)decodeObjectForKey:(NSString *)key; //解码并返回一个与给定键相关联的Object类型的值
- (BOOL)decodeBoolForKey:(NSString *)key; //解码并返回一个与给定键相关联的Bool类型的值
- (int)decodeIntForKey:(NSString *)key; //解码并返回一个与给定键相关联的int类型的值
- (float)decodeFloatForKey:(NSString *)key; //解码并返回一个与给定键相关联的float类型的值
- (double)decodeDoubleForKey:(NSString *)key; //解码并返回一个与给定键相关联的double类型的值
使用归档保存对象,应用如下:
我们创建一个Person类
Person.h文件中
@interface Person : NSObject <NSCoding>
//定义了三个属性
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) float height;
@end
Person.m文件中
#import "Person.h"
@implementation Person
//将一个自定义归档时就会调用该方法,该方法用于描述如何存储自定义对象的属性
-(void)encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject:self.name forKey:@"Name"];
[aCoder encodeInt:self.age forKey:@"Age"];
[aCoder encodeFloat:self.height forKey:@"Height"];
}
//从文件读取一个对象的时候会调用该方法,该方法用于描述如何读取保存在文件中的数据
-(instancetype)initWithCoder:(NSCoder *)aDecoder{
if (self = [super init]) {
self.name = [aDecoder decodeObjectForKey:@"Name"];
self.age = [aDecoder decodeIntForKey:@"Age"];
self.height = [aDecoder decodeFloatForKey:@"Height"];
}
return self;
}
//重写description方法,打印对象的时候,就可以将其所有属性打印出来
-(NSString *)description{
return [NSString stringWithFormat:@"name = %@, age = %d height = %0.1f",_name,_age,_height];
}
ViewController.h 文件中
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end
ViewController.m 文件中
#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
//单击“保存”按钮执行的方法
-(IBAction)save{
//1.创建对象
Person *person = [[Person alloc]init];
person.name = @"Rose";
person.age = 18;
person.height = 48;
//2.获取 Documents 路径
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];
//3.拼接将要保存文件的路径,文件名可以随便取 后缀也可以随便给
NSString *filePath = [path stringByAppendingPathComponent:@"person.data"];
// NSLog(@"%@",filePath);
//4.将对象 person 归档
[NSKeyedArchiver archiveRootObject:person toFile:filePath];
}
//单击“读取”按钮执行的方法
-(IBAction)read{
//1.获取 Documents 路径
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];
//2.拼接保存的文件路径
NSString *filePath = [path stringByAppendingPathComponent:@"person.data"];
//3.从指定的文件中读取对象
Person *person = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
NSLog(@"%@",person);
}
@end
点击“保存”按钮后,Documents 文件家中出现了“person.data”文件
点击“读取”按钮后,成功打印了person 对象属性,说明保存和读取数据都是成功的。
数据持久化的前三种方式(plist、偏好设置、归档)总结完了,后面两种(SQLite、Core Data)将在下篇总结。