概论
所谓的持久化,就是将数据保存到硬盘中,使得在应用程序或机器重启后可以继续访问之前保存的数据。在iOS开发中,常用的5种方案:
- plist文件(属性列表)
- preference(偏好设置)
- NSKeyedArchiver(归档)
- SQLite 3
- CoreData
结构
沙盒就是一个文件夹,目录结构如下:
"应用程序包"
Documents
Library
Caches
Preferences
tmp
目录类型
-
"应用程序包": 这里面存放的是应用程序的源文件,包括资源文件和可执行文件。
NSString *path = [[NSBundle mainBundle] bundlePath]; NSLog(@"%@", path);
-
Documents: 最常用的目录,iTunes同步该应用时会同步此文件夹中的内容,适合存储重要数据。
//第一种方法: NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject; //第二个参数 代表从用户文件夹下找, 第三个参数 代表是否显示路径全称,如果为no,那么输出为~/Documents/... NSLog(@"%@", path); //第二种方法: //获取当前应用沙盒的根目录 NSString *homePath = NSHomeDirectory(); //拼接路径 NSString *docPath = [homePath stringByAppendingPathComponent:@"Documents"];
-
Library/Caches: iTunes不会同步此文件夹,适合存储体积大,不需要备份的非重要数据。
NSString *path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject; NSLog(@"%@", path);
-
Library/Preferences: iTunes同步该应用时会同步此文件夹中的内容,通常保存应用的设置信息。
//NSUserDefaults存储的位置: //<UUID for your App>/Library/Preferences/<your App's bundle ID>.plist
-
tmp: iTunes不会同步此文件夹,系统可能在应用没运行时就删除该目录下的文件,所以此目录适合保存应用中的一些临时文件,用完就删除。
NSString *path = NSTemporaryDirectory(); NSLog(@"%@", path);
存储数据类型和存储方法
- plist文件存取方式(位置任意)
属性列表是一种XML格式的文件,拓展名为plist,可以被序列化的类型只有如下几种:
存储时使用writeToFile: atomically:
方法。 其中atomically
表示是否需要先写入一个辅助文件,再把辅助文件拷贝到目标文件地址。这是更安全的写入文件方法,一般都写YES
。
NSArray;
NSMutableArray;
NSDictionary;
NSMutableDictionary;
NSData;
NSMutableData;
NSString;
NSMutableString;
NSNumber;
-
NSDate;
//1.获得文件路径 NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject; NSString *fileName = [path stringByAppendingPathComponent:@"123.plist"]; //2.存储 NSArray *array = @[@"123", @"456", @"789"]; [array writeToFile:fileName atomically:YES]; //3.读取 NSArray *result = [NSArray arrayWithContentsOfFile:fileName]; NSLog(@"%@", result);
有一点需要注意,就是存取字符串的时候,最好不要使用plist文件,因为plist本身不支持字符串(plist文件其根节点处,只有数组和字典两个选项),而且字符串的存取方法也已经过期。
-
使用NSUserDefaults存储到Preference中(位置固定)
//1.获得NSUserDefaults文件 NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; //2.向文件中写入内容 [userDefaults setObject:@"AAA" forKey:@"a"]; [userDefaults setBool:YES forKey:@"sex"]; [userDefaults setInteger:21 forKey:@"age"]; //2.1立即同步 [userDefaults synchronize]; //3.读取文件 NSString *name = [userDefaults objectForKey:@"a"]; BOOL sex = [userDefaults boolForKey:@"sex"]; NSInteger age = [userDefaults integerForKey:@"age"]; NSLog(@"%@, %d, %ld", name, sex, age);
注意:偏好设置是专门用来保存应用程序的配置信息的,一般不要在偏好设置中保存其他数据。如果没有调用synchronize方法,系统会根据I/O情况不定时刻地保存到文件中。所以如果需要立即写入文件的就必须调用synchronize方法。 偏好设置会将所有数据保存到同一个文件中。即preference目录下的一个以此应用包名来命名的plist文件。
* 使用NSKeyedArchiver归档存储(位置任意)
归档在iOS中是另一种形式的序列化,需要遵循NSCoding协议。
-
遵循NSCoding协议
//1.遵循NSCoding协议 @interface Person : NSObject //2.设置属性 @property (strong, nonatomic) UIImage *avatar; @property (copy, nonatomic) NSString *name; @property (assign, nonatomic) NSInteger age; @end //解档 - (id)initWithCoder:(NSCoder *)aDecoder { if ([super init]) { self.avatar = [aDecoder decodeObjectForKey:@"avatar"]; self.name = [aDecoder decodeObjectForKey:@"name"]; self.age = [aDecoder decodeIntegerForKey:@"age"]; } return self; } //归档 - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:self.avatar forKey:@"avatar"]; [aCoder encodeObject:self.name forKey:@"name"]; [aCoder encodeInteger:self.age forKey:@"age"]; }
特别注意:
如果需要归档的类是某个自定义类的子类时,就需要在归档和解档之前先实现父类的归档和解档方法。即 [super encodeWithCoder:aCoder] 和 [super initWithCoder:aDecoder] 方法;
-
用法
//存 NSString *file = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.data"]; Person *person = [[Person alloc] init]; person.avatar = self.avatarView.image; person.name = self.nameField.text; person.age = [self.ageField.text integerValue]; [NSKeyedArchiver archiveRootObject:person toFile:file]; //取 NSString *file = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.data"]; Person *person = [NSKeyedUnarchiver unarchiveObjectWithFile:file]; if (person) { self.avatarView.image = person.avatar; self.nameField.text = person.name; self.ageField.text = [NSString stringWithFormat:@"%ld", person.age]; }
注意:
- 必须遵循并实现NSCoding协议
- 保存文件的扩展名可以任意指定
- 继承时必须先调用父类的归档解档方法