iOS中經常使用的持久化存儲方式有好幾種:數據庫
偏好設置(NSUserDefaults)數組
plist文件存儲緩存
歸檔安全
SQLite3網絡
Core Dataapp
這裏不細講數據庫,只針對性地講講文件存儲、歸檔/解檔、偏好設置等。dom
在此以前,咱們須要先講講沙盒(Sandbox)才能繼續講解。學習
每一個iOS應用都有本身的應用沙盒(應用沙盒就是文件系統目錄),與其餘文件系統隔離。應用必須待在本身的沙盒裏,其餘應用不能訪問該沙盒。沙盒下的目錄以下:atom
Application:存放程序源文件,上架前通過數字簽名,上架後不可修改spa
Documents: 保存應⽤運行時生成的須要持久化的數據,iTunes同步設備時會備份該目錄。例如,遊戲應用可將遊戲存檔保存在該目錄
tmp: 保存應⽤運行時所需的臨時數據,使⽤完畢後再將相應的文件從該目錄刪除。應用 沒有運行時,系統也可能會清除該目錄下的文件。iTunes同步設備時不會備份該目錄。
Library/Caches: 保存應用運行時⽣成的須要持久化的數據,iTunes同步設備時不會備份 該目錄。⼀通常存儲體積大、不須要備份的非重要數據,好比網絡數據緩存存儲到Caches下
Library/Preference: 保存應用的全部偏好設置,如iOS的Settings(設置) 應⽤會在該目錄中查找應⽤的設置信息。iTunes同步設備時會備份該目錄
NSUserDefaults是個單例類,用於存儲少許數據。NSUserDefaults實際上對plist文件操做的封裝,更方便咱們直接操做,通常用於存儲系統級別的偏好設置。好比咱們常常將登陸後的用戶的一些設置經過NSUserDefaults存儲到plist文件中。
有不少App,他們也是將用戶的帳號和密碼存儲在偏好設置中。咱們不講安全性問題,所以不討論存儲在偏好設置下是否安全。
使用起來很是簡單,以下:
1 2 3 4 5 6 7 8 9 10 11 12 |
// 寫入文件 - (void)saveUserName:(NSString *)userName password:(NSString *)password { [[NSUserDefaults standardUserDefaults] setObject:userName forKey:@"username"]; [[NSUserDefaults standardUserDefaults] setObject:password forKey:@"password"]; [[NSUserDefaults standardUserDefaults] synchronize]; }
// 在用的時候,就能夠讀取出來使用 NSString *userName = [[NSUserDefaults standardUserDefaults] objectForKey:@"username"]; NSString *password = [[NSUserDefaults standardUserDefaults] objectForKey:@"password"];
|
存儲到偏好設置的只有系統已經提供好的類型,好比基本類型、NSNumber、NSDictionary、NSArray等。對於NSObject及繼承於NSObject的類型,是不支持的。以下:
1 2 3 4 5 6 7 |
NSObject *obj = [[NSObject alloc] init]; [[NSUserDefaults standardUserDefaults] setObject:obj forKey:@"obj"];
// 就會崩潰 Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to insert non-property list object <NSObject: 0x7fb502680cb0> for key obj'
|
有的時候,咱們須要將下載的數據存儲到文件中存儲起來,好比,有時候咱們將下載起來的城市的數據存儲到本地,當更新城市的順序時,下次也可以按照最後一次操做的順序來顯示出來。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
// 數據存儲,是保存到手機裏面, // Plist存儲,就是把某些數據寫到plist文件中 // plist存儲通常用來存儲數組和字典 // Plist存儲是蘋果特有,只有蘋果才能生成plist // plist不能存儲自定義對象,如NSObject、model等 NSDictionary *dict = @{@"age":@"18",@"name":@"USER"};
// 保存應用沙盒(app安裝到手機上的文件夾) // Caches文件夾 // 在某個範圍內容搜索文件夾的路徑 // directory:獲取哪一個文件夾 // domainMask:在哪一個範圍下獲取 NSUserDomainMask:在用戶的範圍內搜索 // expandTilde是否展開全路徑,YES:展開 NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; NSLog(@"%@",cachePath);
// 拼接文件路徑 NSString *filePath = [cachePath stringByAppendingPathComponent:@"data.plist"];
// 獲取應用沙盒 NSString *homePath = NSHomeDirectory(); NSLog(@"%@",homePath);
// File:文件全路徑 => 全部文件夾路徑 + 文件路徑 [dict writeToFile:filePath atomically:YES]; // 將數據取出來 NSLog(@"%@", [NSDictionary dictionaryWithContentsOfFile:filePath]);
|
咱們看看打印的結果:
1 2 3 4 5 6 7 8 |
2016-02-17 22:14:43.055 iOSPersistentStorageDemo[25471:809758] /Users/huangyibiao/Library/Developer/CoreSimulator/Devices/CF3A5A4C-486F-4A72-957B-2AD94BD90EC1/data/Containers/Data/Application/65E8F814-45E5-420C-A174-822A7830748E/Library/Caches 2016-02-17 22:14:43.055 iOSPersistentStorageDemo[25471:809758] /Users/huangyibiao/Library/Developer/CoreSimulator/Devices/CF3A5A4C-486F-4A72-957B-2AD94BD90EC1/data/Containers/Data/Application/65E8F814-45E5-420C-A174-822A7830748E 2016-02-17 22:14:43.056 iOSPersistentStorageDemo[25471:809758] { age = 18; name = USER; }
|
注意:操做plist文件時,文件路徑必定要是全路徑。
自定義對象應用範圍很廣,由於它對應着MVC中的Model層,即實體類。在程序中,咱們會在Model層定義不少的entity,例如User、Teacher、Person等。
那麼對自定義對象的歸檔顯得重要的多,由於不少狀況下咱們須要在Home鍵以後保存數據,在程序恢復時從新加載,那麼,歸檔即是一個好的選擇。
下面咱們自定義一個Person類:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// 要使對象能夠歸檔,必須遵照NSCoding協議 @interface Person : NSObject<NSCoding>
@property (nonatomic, assign) int age; @property (nonatomic, strong) NSString *name;
@end
@implementation Person
// 何時調用:只要一個自定義對象歸檔的時候就會調用 - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:self.name forKey:@"name"]; [aCoder encodeInt:self.age forKey:@"age"]; }
- (id)initWithCoder:(NSCoder *)aDecoder { if (self = [super init]) { self.name = [aDecoder decodeObjectForKey:@"name"]; self.age = [aDecoder decodeIntForKey:@"age"]; } return self; } @end
|
如何將自定義對象歸檔和解檔:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
- (void)savePerson { // 歸檔:plist存儲不能存儲自定義對象,此時能夠使用歸檔來完成 Person *person = [[Person alloc] init]; person.age = 18; person.name = @"USER";
// 獲取tmp目錄路徑 NSString *tempPath = NSTemporaryDirectory();
// 拼接文件名 NSString *filePath = [tempPath stringByAppendingPathComponent:@"person.data"];
// 歸檔 [NSKeyedArchiver archiveRootObject:person toFile:filePath]; }
- (void)readPerson { // 獲取tmp NSString *tempPath = NSTemporaryDirectory();
// 拼接文件名 NSString *filePath = [tempPath stringByAppendingPathComponent:@"person.data"];
// 解檔 Person *p = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath]; NSLog(@"%@ %d",p.name,p.age); }
|
假設咱們定義了一個自定義的view,這個view是用xib或者storybard來生成的,那麼咱們我必定以下方法時,就須要以下實現:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@implementation CustomView
// 解析xib,storyboard文件時會調用 - (id)initWithCoder:(NSCoder *)aDecoder { // 何時調用[super initWithCoder:aDecoder]? // 只要父類遵照了NSCoding協議,就調用[super initWithCoder:aDecoder] if (self = [super initWithCoder:aDecoder]) { NSLog(@"%s",__func__); }
return self; }
@end
|
若是想要學習如何經過runtime來實現自動歸檔、解檔,請閱讀文章:經過runtime自動歸檔/解檔