iOS中的數(shù)據(jù)持久化方式

原文: 點(diǎn)擊查看

數(shù)據(jù)持久化 ,就是將數(shù)據(jù)保存起來,以便應(yīng)用程序或設(shè)備重啟后仍然可以訪問

  • plist 文件 (屬性列表)
  • preference (偏好設(shè)置)
  • NSKeyedArchiver (歸檔)
  • SQLite
  • CoreData (它提供了對(duì)象-關(guān)系映射(ORM)的功能,即能夠?qū)C對(duì)象轉(zhuǎn)化成數(shù)據(jù),保存在SQLite數(shù)據(jù)庫文件中,也能夠?qū)⒈4嬖跀?shù)據(jù)庫中的數(shù)據(jù)還原成OC對(duì)象)

沙盒

只允許本程序訪問的目錄

目錄結(jié)構(gòu):

"應(yīng)用程序包"
Documents
Library
    Caches
    Preferences
tmp

應(yīng)用程序包

這里面存放的是應(yīng)用程序的源文件,包括資源文件和可執(zhí)行文件

  NSLog(@"%@",  [[NSBundle mainBundle] bundlePath]);

Documents

最常用的目錄,iTunes同步該應(yīng)用時(shí)會(huì)同步此文件夾中的內(nèi)容,適合存儲(chǔ)重要數(shù)據(jù)。

  NSLog(@"%@", NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject);

Library/Caches

iTunes不會(huì)同步此文件夾,適合存儲(chǔ)體積大,不需要備份的非重要數(shù)據(jù)。

  NSLog(@"%@", NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject);

Library/Preferences

iTunes 會(huì)同步此文件夾, 保存用戶偏好設(shè)置

  NSLog(@"%@", NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject);

tmp

iTunes不會(huì)同步此文件夾,系統(tǒng)可能在應(yīng)用沒運(yùn)行時(shí)就刪除該目錄下的文件,所以此目錄適合保存應(yīng)用中的一些臨時(shí)文件,用完就刪除。

  NSLog(@"%@", NSTemporaryDirectory());

plist 文件

將某些特定的類通過 xml 方式存在目錄中

可被序列化的類型:

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

獲得文件路徑

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

存儲(chǔ)

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

讀取

NSArray *result = [NSArray arrayWithContentsOfFile:fileName];
NSLog(@"%@", result);
  • 只有以上列出的類型才能使用 plist 文件存儲(chǔ)。
  • 存儲(chǔ)時(shí)使用writeToFile: atomically:方法。 其中atomically表示是否需要先寫入一個(gè)輔助文件,再把輔助文件拷貝到目標(biāo)文件地址。這是更安全的寫入文件方法,一般都寫YES。
  • 讀取時(shí)使用arrayWithContentsOfFile:方法。

Preference

//1.獲得NSUserDefaults文件
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
//2.向文件中寫入內(nèi)容
[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);
  • 偏好設(shè)置是專門用來保存應(yīng)用程序的配置信息的,一般不要在偏好設(shè)置中保存其他數(shù)據(jù)。
  • 如果沒有調(diào)用synchronize方法,系統(tǒng)會(huì)根據(jù)I/O情況不定時(shí)刻地保存到文件中。所以如果需要立即寫入文件的就必須調(diào)用synchronize方法。
  • 偏好設(shè)置會(huì)將所有數(shù)據(jù)保存到同一個(gè)文件中。即preference目錄下的一個(gè)以此應(yīng)用包名來命名的plist文件。

歸檔 NSKeyedArchiver

因?yàn)榍皟烧叨加幸粋€(gè)致命的缺陷,只能存儲(chǔ)常用的類型。但只要遵循了NSCoding協(xié)議的對(duì)象都可以通過它實(shí)現(xiàn)序列化。由于決大多數(shù)支持存儲(chǔ)數(shù)據(jù)的Foundation和Cocoa Touch類都遵循了NSCoding協(xié)議, 所以歸檔可以實(shí)現(xiàn)把自定義的對(duì)象存放在文件中。

NSCoding 協(xié)議

NSCoding協(xié)議聲明了兩個(gè)方法,這兩個(gè)方法都是必須實(shí)現(xiàn)的。一個(gè)用來說明如何將對(duì)象編碼到歸檔中,另一個(gè)說明如何進(jìn)行解檔來獲取一個(gè)新對(duì)象。

  • 遵循 NSCoding 協(xié)議
  //1.遵循NSCoding協(xié)議 
  @interface Person : NSObject   //2.設(shè)置屬性
  @property (strong, nonatomic) UIImage *avatar;
  @property (copy, nonatomic) NSString *name;
  @property (assign, nonatomic) NSInteger age;
  @end
  • 實(shí)現(xiàn)協(xié)議方法
// 反歸檔
  - (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"];
  }

如果需要?dú)w檔的類是某個(gè)自定義類的子類時(shí),就需要在歸檔和解檔之前先實(shí)現(xiàn)父類的歸檔和解檔方法。即 super encodeWithCoder:aCodersuper initWithCoder:aDecoder 方法;

  • 歸檔
    需要把對(duì)象歸檔時(shí)調(diào)用NSKeyedArchiver的工廠方法 archiveRootObject: toFile:方法。
  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];
  }

** 注意 **

  • 必須遵循并實(shí)現(xiàn)NSCoding協(xié)議
  • 保存文件的擴(kuò)展名可以任意指定
  • 繼承時(shí)必須先調(diào)用父類的歸檔解檔方法

SQLite 3

之前的所有存儲(chǔ)方法,都是覆蓋存儲(chǔ)。如果想要增加一條數(shù)據(jù)就必須把整個(gè)文件讀出來,然后修改數(shù)據(jù)后再把整個(gè)內(nèi)容覆蓋寫入文件。所以它們都不適合存儲(chǔ)大量的內(nèi)容。

SQLite 中可以將數(shù)據(jù)分為以下類型:

  • integer : 整數(shù)
  • real : 實(shí)數(shù)(浮點(diǎn)數(shù))
  • text : 文本字符串
  • blob : 二進(jìn)制數(shù)據(jù),比如文件,圖片之類的

** 注: ** 主鍵必須設(shè)置成integer.

1. 導(dǎo)入依賴庫

導(dǎo)入 libsqlite3.dylib 并導(dǎo)入主頭文件。

2. 創(chuàng)建數(shù)據(jù)庫并打開

操作數(shù)據(jù)庫之前必須先指定數(shù)據(jù)庫文件和要操作的表,所以使用SQLite3,首先要打開數(shù)據(jù)庫文件,然后指定或創(chuàng)建一張表。

/**
*  打開數(shù)據(jù)庫并創(chuàng)建一個(gè)表
*/
- (void)openDatabase {
   //1.設(shè)置文件名
   NSString *filename = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.db"];
   //2.打開數(shù)據(jù)庫文件,如果沒有會(huì)自動(dòng)創(chuàng)建一個(gè)文件
   NSInteger result = sqlite3_open(filename.UTF8String, &_sqlite3);
   if (result == SQLITE_OK) {
       NSLog(@"打開數(shù)據(jù)庫成功!");
       //3.創(chuàng)建一個(gè)數(shù)據(jù)庫表
       char *errmsg = NULL;
       sqlite3_exec(_sqlite3, "CREATE TABLE IF NOT EXISTS t_person(id integer primary key autoincrement, name text, age integer)", NULL, NULL, &errmsg);
       if (errmsg) {
           NSLog(@"錯(cuò)誤:%s", errmsg);
       } else {
           NSLog(@"創(chuàng)表成功!");
       }
   } else {
       NSLog(@"打開數(shù)據(jù)庫失敗!");
   }
}

3. 執(zhí)行命令

使用 sqlite3_exec() 方法可以執(zhí)行任何SQL語句,比如創(chuàng)表、更新、插入和刪除操作。但是一般不用它執(zhí)行查詢語句,因?yàn)樗粫?huì)返回查詢到的數(shù)據(jù)。

// 往表中插入數(shù)據(jù)
 NSString *sql = [NSString stringWithFormat:@"INSERT INTO t_person (name, age) VALUES('%@', '%ld')", nameStr, age];
  char *errmsg = NULL;
  sqlite3_exec(_sqlite3, sql.UTF8String, NULL, NULL, &errmsg);

4. 查詢命令

  • sqlite3_prepare_v2() : 檢查sql的合法性
  • sqlite3_step() : 逐行獲取查詢結(jié)果,不斷重復(fù),直到最后一條記錄
  • sqlite3_coloum_xxx() : 獲取對(duì)應(yīng)類型的內(nèi)容,iCol對(duì)應(yīng)的就是 SQL語句中字段的順序,從0開始。根據(jù)實(shí)際查詢字段的屬性,使用sqlite3_column_xxx取得對(duì)應(yīng)的內(nèi)容即可。
  • sqlite3_finalize() : 釋放stmt
/**
*  從表中讀取數(shù)據(jù)到數(shù)組中
*/
- (void)readData {
   NSMutableArray *mArray = [NSMutableArray arrayWithCapacity:1000];
   char *sql = "select name, age from t_person;";
   sqlite3_stmt *stmt;
   NSInteger result = sqlite3_prepare_v2(_sqlite3, sql, -1, &stmt, NULL);
   if (result == SQLITE_OK) {
       while (sqlite3_step(stmt) == SQLITE_ROW) {
           char *name = (char *)sqlite3_column_text(stmt, 0);
           NSInteger age = sqlite3_column_int(stmt, 1);
           //創(chuàng)建對(duì)象
           Person *person = [Person personWithName:[NSString stringWithUTF8String:name] Age:age];
           [mArray addObject:person];
       }
       self.dataList = mArray;
   }
   sqlite3_finalize(stmt);
}

CoreData

CoreData 參考這位大神的簡書, 寫得非常詳細(xì).
鏈接: 我要娶你做我的CoreData! 原文

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容