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

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

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

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

沙盒

只允許本程序訪問的目錄

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

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

應(yīng)用程序包

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

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

Documents

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

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

Library/Caches

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

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

Library/Preferences

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

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

tmp

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

  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"];

存儲

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

讀取

NSArray *result = [NSArray arrayWithContentsOfFile:fileName];
NSLog(@"%@", result);
  • 只有以上列出的類型才能使用 plist 文件存儲。
  • 存儲時使用writeToFile: atomically:方法近她。 其中atomically表示是否需要先寫入一個輔助文件叉瘩,再把輔助文件拷貝到目標(biāo)文件地址。這是更安全的寫入文件方法粘捎,一般都寫YES薇缅。
  • 讀取時使用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)會根據(jù)I/O情況不定時刻地保存到文件中。所以如果需要立即寫入文件的就必須調(diào)用synchronize方法灸撰。
  • 偏好設(shè)置會將所有數(shù)據(jù)保存到同一個文件中谒府。即preference目錄下的一個以此應(yīng)用包名來命名的plist文件。

歸檔 NSKeyedArchiver

因為前兩者都有一個致命的缺陷浮毯,只能存儲常用的類型完疫。但只要遵循了NSCoding協(xié)議的對象都可以通過它實(shí)現(xiàn)序列化。由于決大多數(shù)支持存儲數(shù)據(jù)的Foundation和Cocoa Touch類都遵循了NSCoding協(xié)議, 所以歸檔可以實(shí)現(xiàn)把自定義的對象存放在文件中债蓝。

NSCoding 協(xié)議

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

  • 遵循 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檔的類是某個自定義類的子類時,就需要在歸檔和解檔之前先實(shí)現(xiàn)父類的歸檔和解檔方法蹦锋。即 super encodeWithCoder:aCodersuper initWithCoder:aDecoder 方法;

  • 歸檔
    需要把對象歸檔時調(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ò)展名可以任意指定
  • 繼承時必須先調(diào)用父類的歸檔解檔方法

SQLite 3

之前的所有存儲方法,都是覆蓋存儲莉掂。如果想要增加一條數(shù)據(jù)就必須把整個文件讀出來葛圃,然后修改數(shù)據(jù)后再把整個內(nèi)容覆蓋寫入文件。所以它們都不適合存儲大量的內(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)建一個表
*/
- (void)openDatabase {
   //1.設(shè)置文件名
   NSString *filename = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.db"];
   //2.打開數(shù)據(jù)庫文件,如果沒有會自動創(chuàng)建一個文件
   NSInteger result = sqlite3_open(filename.UTF8String, &_sqlite3);
   if (result == SQLITE_OK) {
       NSLog(@"打開數(shù)據(jù)庫成功鹤树!");
       //3.創(chuàng)建一個數(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(@"錯誤:%s", errmsg);
       } else {
           NSLog(@"創(chuàng)表成功铣焊!");
       }
   } else {
       NSLog(@"打開數(shù)據(jù)庫失敗罕伯!");
   }
}

3. 執(zhí)行命令

使用 sqlite3_exec() 方法可以執(zhí)行任何SQL語句曲伊,比如創(chuàng)表、更新追他、插入和刪除操作坟募。但是一般不用它執(zhí)行查詢語句岛蚤,因為它不會返回查詢到的數(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() : 獲取對應(yīng)類型的內(nèi)容,iCol對應(yīng)的就是 SQL語句中字段的順序昂利,從0開始届腐。根據(jù)實(shí)際查詢字段的屬性,使用sqlite3_column_xxx取得對應(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)建對象
           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)容合作請聯(lián)系作者
  • 序言:七十年代末硬萍,一起剝皮案震驚了整個濱河市扩所,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌朴乖,老刑警劉巖祖屏,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異买羞,居然都是意外死亡袁勺,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進(jìn)店門畜普,熙熙樓的掌柜王于貴愁眉苦臉地迎上來期丰,“玉大人,你說我怎么就攤上這事吃挑《鄣矗” “怎么了?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵舶衬,是天一觀的道長埠通。 經(jīng)常有香客問我,道長逛犹,這世上最難降的妖魔是什么端辱? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮虽画,結(jié)果婚禮上舞蔽,老公的妹妹穿的比我還像新娘。我一直安慰自己狸捕,他們只是感情好喷鸽,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著灸拍,像睡著了一般做祝。 火紅的嫁衣襯著肌膚如雪砾省。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天混槐,我揣著相機(jī)與錄音编兄,去河邊找鬼。 笑死声登,一個胖子當(dāng)著我的面吹牛狠鸳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播悯嗓,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼件舵,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了脯厨?” 一聲冷哼從身側(cè)響起铅祸,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎合武,沒想到半個月后临梗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡稼跳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年盟庞,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片汤善。...
    茶點(diǎn)故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡什猖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出萎津,到底是詐尸還是另有隱情卸伞,我是刑警寧澤,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布锉屈,位于F島的核電站荤傲,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏颈渊。R本人自食惡果不足惜遂黍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望俊嗽。 院中可真熱鬧雾家,春花似錦、人聲如沸绍豁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至敬飒,卻和暖如春邪铲,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背无拗。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工带到, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人英染。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓揽惹,卻偏偏與公主長得像,于是被迫代替她去往敵國和親四康。 傳聞我的和親對象是個殘疾皇子搪搏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評論 2 359

推薦閱讀更多精彩內(nèi)容