1. 程式人生 > 其它 >iOS學習——iOS常用的儲存方式

iOS學習——iOS常用的儲存方式

不管是在iOS還是Android開發過程中,我們都經常性地需要儲存一些狀態和資料,比如使用者對於App的相關設定、需要在本地快取的資料等等。根據要儲存的的資料的大小、儲存性質以及儲存型別,在iOS和Android中哪個都有多種儲存方式。其中,iOS中的儲存方式主要包括以下六類:

  • plist檔案(屬性列表)
  • preference(偏好設定)
  • NSKeyedArchiver(歸檔)
  • SQLite 3
  • CoreData
  • 手動存放沙盒

一、沙盒機制

在研究儲存方式之前,我們有必要先研究下這些檔案會儲存到什麼地方去,這就需要我們瞭解iOS App特有的沙盒機制了。iOS程式預設情況下只能訪問程式自己的目錄,這個目錄被稱為“沙盒”,即沙盒其實就是一個App特有的一個資料夾,iOS下每個App都有自己特有的一個沙盒,其結構和目錄特性都是一樣的。

1.1 沙盒結構

  既然沙盒就是一個資料夾,那就看看裡面有什麼吧。沙盒的目錄結構如下圖所示,每個App的沙盒都是由下圖所示的四部分組成,每一部分中存放的資料和內容都是有一定的規範和性質的。該目錄路徑的獲取方法是直接通過 NSHomeDirectory() 就得到和應用沙盒的路徑。

  此外,每一個App還有一個Bundle目錄,即“應用程式包”,該目錄下 存放的是應用程式的原始檔,包括資原始檔和可執行檔案。

1.2 沙盒目錄特性

  雖然沙盒中有這麼多資料夾,但是沒有資料夾都不盡相同,都有各自的特性。所以在選擇存放目錄時,一定要認真選擇適合的目錄。

  • 應用程式包:存放的是應用程式的原始檔,包括資原始檔和可執行檔案。如果你要仿寫某一個App或借用某個App的應用圖示,可以在該App的應用程式包中找到其.app結尾的原始檔,然後顯示報內容即可直接獲取到其所有的圖示和應用切圖。在開發中獲取其bundle(應用程式包)路徑的方法是:
NSString *path = [[NSBundle mainBundle] bundlePath];
NSLog(@"%@", path);
  • Documents: 最常用的目錄,iTunes同步該應用時會同步此資料夾中的內容,適合儲存重要資料。如果自己儲存log資料到本地,一般是儲存到該路徑下。獲取路徑下的方法是:
NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSLog(@"%@", path);
  • Library/Caches
    : iTunes不會同步此資料夾,適合儲存體積大,不需要備份的非重要資料。
NSString *path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
NSLog(@"%@", path);
  • Library/Preferences: iTunes同步該應用時會同步此資料夾中的內容,通常儲存應用的設定資訊。
  • tmp:iTunes不會同步此資料夾,系統可能在應用沒執行時就刪除該目錄下的檔案,所以此目錄適合儲存應用中的一些臨時檔案,用完就刪除。
NSString *path = NSTemporaryDirectory();
NSLog(@"%@", path);

二、儲存方式

在文章的開始已經講到了,iOS中本地儲存的方式一般有6種。下面我們將一個個來進行學習和研究。

2.1 plist檔案(屬性列表)

plist檔案是將某些特定的類,通過XML檔案的方式儲存在目錄中。可以被序列化的型別只有如下幾種:

  • NSArray
  • NSMutableArray
  • NSDictionary
  • NSMutableDictionary
  • NSData
  • NSMutableData
  • NSString
  • NSMutableString
  • NSNumber
  • NSDate

1. 獲得檔案路徑

專案中plist檔案是儲存在沙盒的documents中,所以要獲取某個plist檔案,只需要知道其檔名就可以了,如下方式就好可以獲取並讀取其中的內容,讀取時通過對應型別的方式來獲取plist的資料。一般plist中的內容都是以NSArray或NSDictionary的形式儲存。

NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSString *fileName = [path stringByAppendingPathComponent:@"123.plist"];
NSArray *result = [NSArray arrayWithContentsOfFile:fileName];

2. 儲存

往plist中寫內容也非常簡單,直接用相應型別的writeToFile方法即可。

NSArray *array = @[@"123", @"456", @"789"];
[array writeToFile:fileName atomically:YES]; 

3. 注意

  • 只有以上列出的型別才能使用plist檔案儲存。
  • 儲存時使用writeToFile: atomically:方法。 其中atomically表示是否需要先寫入一個輔助檔案,再把輔助檔案拷貝到目標檔案地址。這是更安全的寫入檔案方法,一般都寫YES。
  • 讀取時使用arrayWithContentsOfFile:方法

2.2 preference(偏好設定)

preefrence(偏好設定)顧名思義就是使用者在使用過程中對App的一些狀態和自定義設定狀態的儲存,例如App的面板樣式、遊戲時是否遮蔽電話和聊天、介面顯示格式等等。一般對於一些基本的使用者設定,因為資料量很小,我們可以使用OC語言中的NSUserDefaults類來進行處理。使用方法很簡單,只需要呼叫類中的方法即可。此外,NSUserDefaults 建立的資料其實也是一個plist檔案,其中資料儲存格式是鍵值對形式,即NSDictionary形式,該檔案存放在沙盒 Library/Preferences/ 目錄下,一個以你包名命名的.plist檔案。

1. 使用方法

//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);

2. 注意

  • 偏好設定是專門用來儲存應用程式的配置資訊的,一般不要在偏好設定中儲存其他資料。
  • 如果沒有呼叫synchronize方法,系統會根據I/O情況不定時刻地儲存到檔案中。所以如果需要立即寫入檔案的就必須呼叫synchronize方法。
  • 偏好設定會將所有資料儲存到同一個檔案中。即preference目錄下的一個以此應用包名來命名的plist檔案。

2.3  NSKeyedArchiver(歸檔)

之前說了,不管是NSUserDefaults 或者是 plist 都不能對自定義的物件進行儲存,OC提供瞭解歸檔恰好解決這個問題。歸檔在iOS中是另一種形式的序列化,只要遵循了NSCoding協議的物件都可以通過它實現序列化。由於決大多數支援儲存資料的Foundation和Cocoa Touch類都遵循了NSCoding協議,因此,對於大多數類來說,歸檔相對而言還是比較容易實現的。

1. 遵循NSCoding協議

NSCoding協議聲明瞭兩個方法,這兩個方法都是必須實現的。一個用來說明如何將物件編碼到歸檔中,另一個說明如何進行解檔來獲取一個新物件。

  • 遵循協議和設定屬性
1  //1.遵循NSCoding協議 
2   @interface Person : NSObject   //2.設定屬性
3   @property (strong, nonatomic) UIImage *avatar;
4   @property (copy, nonatomic) NSString *name;
5   @property (assign, nonatomic) NSInteger age;
6   @end
  • 實現協議方法
 1 //解檔
 2   - (id)initWithCoder:(NSCoder *)aDecoder {
 3       if ([super init]) {
 4           self.avatar = [aDecoder decodeObjectForKey:@"avatar"];
 5           self.name = [aDecoder decodeObjectForKey:@"name"];
 6           self.age = [aDecoder decodeIntegerForKey:@"age"];
 7       }
 8       return self;
 9   }
10   //歸檔
11   - (void)encodeWithCoder:(NSCoder *)aCoder {
12       [aCoder encodeObject:self.avatar forKey:@"avatar"];
13       [aCoder encodeObject:self.name forKey:@"name"];
14       [aCoder encodeInteger:self.age forKey:@"age"];
15   }
  •  特別注意

如果需要歸檔的類是某個自定義類的子類時,就需要在歸檔和解檔之前先實現父類的歸檔和解檔方法。即 [super encodeWithCoder:aCoder] 和 [super initWithCoder:aDecoder] 方法。

2. 使用

  需要把物件歸檔是呼叫NSKeyedArchiver的工廠方法 archiveRootObject: toFile: 方法。

1 NSString *file = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.data"];
2  Person *person = [[Person alloc] init];
3  person.avatar = self.avatarView.image;
4  person.name = self.nameField.text;
5  person.age = [self.ageField.text integerValue];
6  [NSKeyedArchiver archiveRootObject:person toFile:file];

  需要從檔案中解檔物件就呼叫NSKeyedUnarchiver的一個工廠方法 unarchiveObjectWithFile: 即可。

1 NSString *file = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.data"];
2 Person *person = [NSKeyedUnarchiver unarchiveObjectWithFile:file];
3 if (person) {
4      self.avatarView.image = person.avatar;
5      self.nameField.text = person.name;
6      self.ageField.text = [NSString stringWithFormat:@"%ld", person.age];
7 }

3. 注意

2.4 手動存放沙盒

手動將資料存放到沙盒,其實就是自己在沙盒的某一個指定路徑(第一部分介紹了沙盒各目錄路徑的獲取方式)下新建一個儲存資料的檔案(.txt、.plist、.data等格式的檔案),然後向其中寫我們需要儲存的資料即可。但是沙盒中只能儲存OC中的基本資料,自定義的物件不能直接存入,但是可以通過歸檔存為.data檔案。 

1  //假設我們需往cache 存入資料,並命名為test的txt格式檔案中
2 NSString *filePath = [cachesDir stringByAppendingPathComponent:@"test.txt"];
3 NSArray *dic = [[NSArray alloc] initWithObjects:@"test",@"test1" ,nil];
4     
5 if([dic writeToFile:filePath atomically:YES]){
6     NSLog(@"存入成功");
7 }
8 //取出資料 列印
9 NSLog(@"%@",[NSArray arrayWithContentsOfFile:filePath]); 

2.5 CoreData

Core Date是ios3.0後引入的資料持久化解決方案,它是是蘋果官方推薦使用的,不需要藉助第三方框架。Core Date實際上是對SQLite的封裝,提供了更高階的持久化方式。在對資料庫操作時,不需要使用sql語句,也就意味著即使不懂sql語句,也可以操作資料庫中的資料。

  在各類應用開發中使用資料庫操作時通常都會用到 (ORM) “物件關係對映”,Core Data就是這樣的一種模式。ORM是將關係資料庫中的表,轉化為程式中的物件,但實際上是對資料中的資料進行操作。

  在使用Core Data進⾏行資料庫存取並不需要手動建立資料庫,建立資料庫的過程完全由Core Data框架自動完成,開發者需要做的就是把模型建立起來,具體資料庫的建立不需要管。簡單點說,Core Data實際上是將資料庫的建立、表的建立、物件和表的轉換等操作封裝起來,極大的簡化了我們的操作。

  Core Date與SQLite相比較,SQLite比較原始,操作比較複雜,使用的是C的函式對資料庫進行操作,但是SQLite可控性更強,並且能夠跨平臺。

  關於Core Date的具體使用方法參見:IOS 資料儲存之 Core Data詳解

2.6 SQLite 3

  iOS系統自帶Core Data來進行持久化處理,而且Core Data可以使用圖形化介面來建立物件,但是Core Data不是關係型資料庫,對於Core Data來說比較擅長管理在裝置上建立的資料持久化儲存使用者建立的物件,但是要處理大量的資料時就應該優先選擇SQL關係型資料庫來儲存這些資料。    Core Data在後臺也是使用SQLite來儲存資料的,但是開發人員不能直接訪問這些資料,只能通過Core Data提供的API來操作,如果一旦人為的通過SQLite修改這些資料那麼使用Core Data再次訪問這些資料時就會發生錯誤。

SQLite是使用C語言寫的開源庫,實現了一個自包含的SQL關係型資料庫引擎,可以使用SQLite儲存操作大量的資料,作為關係型資料庫我們可以在一個數據庫中建立多張相關聯的表來解決大量資料重複的問題。而且SQLite庫也針對移動裝置上的使用進行了優化。 因為SQLite的介面使用C寫的,而且Objective-CC的超集所以可以直接在專案中使用SQLite

  關於SQLite的詳細使用方法詳見:iOS開發資料庫篇—SQLite的應用