iOS常用的持久化存儲方式

GloriaLees 8年前發布 | 11K 次閱讀 iOS開發 移動開發

來自: http://www.henishuo.com/ios-persistent-storage/

前言

iOS中常用的持久化存儲方式有好幾種:

  • 偏好設置(NSUserDefaults)
  • plist文件存儲
  • 歸檔
  • SQLite3
  • Core Data

這里不細講數據庫,只針對性地講講文件存儲、歸檔/解檔、偏好設置等。

在此之前,我們需要先講講沙盒(Sandbox)才能繼續講解。

沙盒

每個iOS應用都有自己的應用沙盒(應用沙盒就是文件系統目錄),與其他文件系統隔離。應用必須待在自己的沙盒里,其他應用不能訪問該沙盒。沙盒下的目錄如下:

  • Application:存放程序源文件,上架前經過數字簽名,上架后不可修改
  • Documents: 保存應?運行時生成的需要持久化的數據,iTunes同步設備時會備份該目錄。例如,游戲應用可將游戲存檔保存在該目錄
  • tmp: 保存應?運行時所需的臨時數據,使?完畢后再將相應的文件從該目錄刪除。應用 沒有運行時,系統也可能會清除該目錄下的文件。iTunes同步設備時不會備份該目錄。
  • Library/Caches: 保存應用運行時?成的需要持久化的數據,iTunes同步設備時不會備份 該目錄。?一般存儲體積大、不需要備份的非重要數據,比如網絡數據緩存存儲到Caches下
  • Library/Preference: 保存應用的所有偏好設置,如iOS的Settings(設置) 應?會在該目錄中查找應?的設置信息。iTunes同步設備時會備份該目錄

NSUserDefaults

NSUserDefaults是個單例類,用于存儲少量數據。NSUserDefaults實際上對plist文件操作的封裝,更方便我們直接操作,一般用于存儲系統級別的偏好設置。比如我們經常將登錄后的用戶的一些設置通過NSUserDefaults存儲到plist文件中。

有很多App,他們也是將用戶的賬號和密碼存儲在偏好設置中。我們不講安全性問題,因此不討論存儲在偏好設置下是否安全。

使用起來非常簡單,如下:

 
// 寫入文件
- (void)saveUserName:(NSString *)userNamepassword:(NSString *)password {
  [[NSUserDefaults standardUserDefaults]setObject:userNameforKey:@"username"];
  [[NSUserDefaults standardUserDefaults]setObject:passwordforKey:@"password"];
  [[NSUserDefaults standardUserDefaults]synchronize];
}
 
// 在用的時候,就可以讀取出來使用
NSString *userName = [[NSUserDefaults standardUserDefaults]objectForKey:@"username"];
NSString *password = [[NSUserDefaults standardUserDefaults]objectForKey:@"password"];
 

存儲到偏好設置的只有系統已經提供好的類型,比如基本類型、NSNumber、NSDictionary、NSArray等。對于NSObject及繼承于NSObject的類型,是不支持的。如下:

 
NSObject *obj = [[NSObject alloc]init];
[[NSUserDefaults standardUserDefaults]setObject:objforKey:@"obj"];
 
// 就會崩潰
Terminating appduetouncaughtexception 'NSInvalidArgumentException', reason: 'Attempt to insert non-property list object <NSObject: 0x7fb502680cb0> for key obj'
 

plist存儲

有的時候,我們需要將下載的數據存儲到文件中存儲起來,比如,有時候我們將下載起來的城市的數據存儲到本地,當更新城市的順序時,下次也能夠按照最后一次操作的順序來顯示出來。

 
// 數據存儲,是保存到手機里面,
// 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 = [cachePathstringByAppendingPathComponent:@"data.plist"];
  
// 獲取應用沙盒
NSString *homePath = NSHomeDirectory();
NSLog(@"%@",homePath);
  
// File:文件全路徑 => 所有文件夾路徑 + 文件路徑
[dictwriteToFile:filePathatomically:YES];
// 將數據取出來
NSLog(@"%@", [NSDictionarydictionaryWithContentsOfFile:filePath]);
 

我們看看打印的結果:

 
2016-02-1722: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-1722: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-1722:14:43.056 iOSPersistentStorageDemo[25471:809758] {
    age = 18;
    name = USER;
}
 

注意:操作plist文件時,文件路徑一定要是全路徑。

歸檔(NSKeyedArchiver)

自定義對象應用范圍很廣,因為它對應著MVC中的Model層,即實體類。在程序中,我們會在Model層定義很多的entity,例如User、Teacher、Person等。

那么對自定義對象的歸檔顯得重要的多,因為很多情況下我們需要在Home鍵之后保存數據,在程序恢復時重新加載,那么,歸檔便是一個好的選擇。

下面我們自定義一個Person類:

 
// 要使對象可以歸檔,必須遵守NSCoding協議
@interfacePerson: NSObject<NSCoding>
 
@property (nonatomic, assign) int age;
@property (nonatomic, strong) NSString *name;
 
@end
 
@implementation Person
 
// 什么時候調用:只要一個自定義對象歸檔的時候就會調用
- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoderencodeObject:self.nameforKey:@"name"];
    [aCoderencodeInt:self.ageforKey:@"age"];
}
 
- (id)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super init]) {
      self.name = [aDecoderdecodeObjectForKey:@"name"];
      self.age = [aDecoderdecodeIntForKey:@"age"];
    }
    return self;
}
@end
 

如何將自定義對象歸檔和解檔:

 
- (void)savePerson { 
    // 歸檔:plist存儲不能存儲自定義對象,此時可以使用歸檔來完成
    Person *person = [[Person alloc]init];
    person.age = 18;
    person.name = @"USER";
        
    // 獲取tmp目錄路徑
    NSString *tempPath = NSTemporaryDirectory();
        
    // 拼接文件名
    NSString *filePath = [tempPathstringByAppendingPathComponent:@"person.data"];
        
    // 歸檔
    [NSKeyedArchiverarchiveRootObject:persontoFile:filePath];
}
 
- (void)readPerson {
    // 獲取tmp  
    NSString *tempPath = NSTemporaryDirectory();
        
    // 拼接文件名
    NSString *filePath = [tempPathstringByAppendingPathComponent:@"person.data"];
        
    // 解檔
    Person *p = [NSKeyedUnarchiverunarchiveObjectWithFile:filePath];
    NSLog(@"%@ %d",p.name,p.age);
}
 

假設我們定義了一個自定義的view,這個view是用xib或者storybard來生成的,那么我們我一定如下方法時,就需要如下實現:

 
@implementation CustomView
 
// 解析xib,storyboard文件時會調用
- (id)initWithCoder:(NSCoder *)aDecoder {
    // 什么時候調用[super initWithCoder:aDecoder]?
    // 只要父類遵守了NSCoding協議,就調用[super initWithCoder:aDecoder]
    if (self = [superinitWithCoder:aDecoder]) {
        NSLog(@"%s",__func__);
    }
    
    return  self;
}
 
@end
 

如果想要學習如何通過runtime來實現自動歸檔、解檔,請閱讀文章: 通過runtime自動歸檔/解檔

最后

關于數據庫,這里不講解了,因為數據庫是一個很大方面的知識,不是三言兩語所能講通的。

關注我

關注 賬號 備注
Swift/ObjC技術群一 324400294 群一若已滿,請申請群二
Swift/ObjC技術群二 494669518 群二若已滿,請申請群三
Swift/ObjC技術群三 461252383 群三若已滿,會有提示信息
關注微信公眾號 iOSDevShares 關注微信公眾號,會定期地推送好文章
關注新浪微博賬號 標哥Jacky 關注微博,每次發布文章都會分享到新浪微博
關注標哥的GitHub CoderJackyHuang 這里有很多的Demo和開源組件
關于我 進一步了解標哥 如果覺得文章對您很有幫助,可捐助我!

版權聲明:本文為【標哥的技術博客】原創出品,歡迎轉載,轉載時請注明出處!

</div>

 本文由用戶 GloriaLees 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!