1、概念
數(shù)據(jù)持久化-將數(shù)據(jù)保存在硬盤中,使得機器/應用程序重啟后,可以繼續(xù)訪問之前保存的數(shù)據(jù)。
2、方案
plist(屬性列表)、preference(偏好設置)、NSKeyedArchiver(歸檔)、SQLite 3(數(shù)據(jù)庫)、CoreData
3、沙盒機制
3.1、應用程序的目錄路經
NSString *appPath = NSHomeDirectory();
appPath:/Users/lizhongfeng/Library/Developer/CoreSimulator/Devices/6E35AB76-8538-4BB8-9F2A-1574A807C05B/data/Containers/Data/Application/39D4B302-E8BA-4DC0-A9A2-E47DCCD89615
上面的代碼得到的是app的目錄路徑,即為該app的沙盒路徑。
3.2、沙盒的目錄結構
雖然沙盒中文件夾較多,但這些文件夾不盡相同,所存放的數(shù)據(jù)類型不同。因此,存放數(shù)據(jù)時,應根據(jù)數(shù)據(jù)類型和想要達到的效果,選擇合適的文件夾。
3.2.1、Documents
最常用的目錄,iTunes同步該應用時,會同步此文件夾中的內容,適合存儲重要數(shù)據(jù);
NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
documentsPath:/Users/lizhongfeng/Library/Developer/CoreSimulator/Devices/6E35AB76-8538-4BB8-9F2A-1574A807C05B/data/Containers/Data/Application/0967BD85-9B06-449B-A1F0-27B542BAC69A/Documents
3.2.2、Library/Caches
iTunes不會同步此文件夾,適合存儲體積大,但無需備份的非重要數(shù)據(jù);
NSString *cachesPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
cachesPath:/Users/lizhongfeng/Library/Developer/CoreSimulator/Devices/6E35AB76-8538-4BB8-9F2A-1574A807C05B/data/Containers/Data/Application/0967BD85-9B06-449B-A1F0-27B542BAC69A/Library/Caches
3.2.3、Library/Preferences
iTunes同步該應用時,會同步此文件夾中的內容,通常保存應用的設置信息;
3.2.4、tmp
iTunes不會同步此文件夾,系統(tǒng)可能在應用沒運行時,就刪除該目錄下的文件,所以該目錄適合保存應用中的一些臨時文件,用完就刪除。
NSString *tmpPath = NSTemporaryDirectory();
tmpPath:/Users/lizhongfeng/Library/Developer/CoreSimulator/Devices/6E35AB76-8538-4BB8-9F2A-1574A807C05B/data/Containers/Data/Application/5A198C87-225D-4263-BCDF-9CC28A9DFB4F/tmp/
4、plist文件
plist文件是將某些類,通過XML文件的形式保存在目錄中。
4.1、可以被序列化的類型
NSArray/NSMutableArray、NSDictionary/NSMutableDictionary、NSData/NSMutableData、NSString/NSMutableString、NSNumber、NSDate,只有以上類型才能使用plist文件存儲。
4.2、存儲步驟
// 拼接文件路徑
NSString *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSString *filePath = [docPath stringByAppendingPathComponent:@"123.plist"];
// 存儲
NSArray *array = @[@"123",@"456",@"zhongfeng"];
[array writeToFile:filePath atomically:YES];
// 讀取
NSArray *result = [NSArray arrayWithContentsOfFile:filePath];
NSLog(@"result:%@",result);
注意:
存儲時使用writeToFile: atomically:方法。其中,atomically:表示是否先寫入一個臨時文件,再將該臨時文件拷貝到目標文件地址。將其設置為YES是一種更安全的文件寫入方法,所以一般都寫YES。
讀取文件使用***WithContentsOfFile:(arrayWithContentsOfFile:,dictionaryWithContentsOfFile:)方法。
5、Preference
5.1、使用方法
// 獲得NSUserDefaults文件
NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
// 項文件中寫入內容
[userDefault setObject:@"zhongfeng" forKey:@"name"];
[userDefault setInteger:27 forKey:@"age"];
[userDefault setBool:YES forKey:@"isMan"];
// 同步(手動調用synchronize方法以及時保存userDefault文件內容)
[userDefault synchronize];
// 讀取
NSString *name = [userDefault stringForKey:@"name"];
NSInteger age = [userDefault integerForKey:@"age"];
BOOL isMan = [userDefault boolForKey:@"isMan"];
NSLog(@"name = %@, age = %ld, isMan = %d",name,(long)age,isMan);
注意:
偏好設置一般是用來保存應用程序的配置信息,不要在偏好設置中保存其他數(shù)據(jù)。
如果沒有調用synchronize方法進行同步,則NSUserDefaults會過一段時間自動調用synchronize方法進行同步。因此,如果需要立即寫入文件就必須馬上調用synchronize方法。
偏好設置會將所有數(shù)據(jù)保存在Preferences中一個以應用程序包名(Bundle Identifier)的plist文件。因此,偏好設置所能存儲的數(shù)據(jù)類型肯定是plist文件所支持的類型。
6、NSKeyedArchiver
歸檔是iOS中另一種形式化的序列化,只要遵循了NSCoding協(xié)議的對象都可以通過它實現(xiàn)序列化。由于絕大數(shù)支持存儲數(shù)據(jù)的Foundation和Coca? Touch類都遵循了NSCoding協(xié)議。因此,對于大多數(shù)類而言,歸檔還是比較容易實現(xiàn)。
6.1、NSCoding
NSCoding協(xié)議聲明了兩個方法,這兩個方法都必須要實現(xiàn)。一個方法說明如何將對象編碼到歸檔中;另一個方法說明如何解檔獲取一個對象。
/*遵循協(xié)議、設置屬性*/
#import@interface Person : NSObject// 遵循NSCoding協(xié)議
// 屬性
@property (nonatomic,copy) NSString *name;
@property (nonatomic,assign) NSInteger age;
@property (nonatomic,assign) BOOL isMan;
// 初始化Person
+(instancetype)initPersonWithName:(NSString *)name age:(NSInteger)age isMan:(BOOL)isMan;
@end
/*實現(xiàn)NSCoding的兩個方法*/
#import "Person.h"
@implementation Person
// 解檔
-(instancetype)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super init]) {
self.name = [aDecoder decodeObjectForKey:@"name"];
self.age = [aDecoder decodeIntegerForKey:@"age"];
self.isMan = [aDecoder decodeBoolForKey:@"isMan"];
}
return self;
}
// 歸檔
-(void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.name forKey:@"name"];
[aCoder encodeInteger:self.age forKey:@"age"];
[aCoder encodeBool:self.isMan forKey:@"isMan"];
}
注意:
如果需要歸檔的類是某個自定義類的子類時,就需要在歸檔和解檔之前先調用父類的[super initWithCoder:aDecoder]和[super encodeWithCoder:aCoder]方法;
/*使用*/
歸檔對象是調用NSKeyedArchiver的類方法archiveRootObject: toFile:方法,解檔對象是調用NSKeyedUnarchiver的類方法unarchiveObjectWithFile:方法。
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// 創(chuàng)建文件保存路徑
NSString *personInfoPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"personInfo.data"];
// 實例化一個person
Person *person = [Person initPersonWithName:@"宣曉" age:27 isMan:NO];
// 歸檔
[self archiverPerson:person filePath:personInfoPath];
// 解檔
[self unarchiverPersonWithFilePath:personInfoPath];
}
#pragma mark - 歸檔對象
-(void)archiverPerson:(Person *)person filePath:(NSString *)filePath
{
// 歸檔
[NSKeyedArchiver archiveRootObject:person toFile:filePath];
}
#pragma mark - 解檔對象
-(void)unarchiverPersonWithFilePath:(NSString *)filePath
{
// 解檔
Person *person2 = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
if (person2) {
NSLog(@"name:%@,age:%ld,isMan:%d",person2.name,(long)person2.age,person2.isMan);
}
}
注意:
使用歸檔和解檔的類,必須遵循并實現(xiàn)NSCoding協(xié)議;
所保存文件的擴展名可以任意指定;
若有繼承,必須先調用父類的歸檔和解檔方法。