iOS-4種基本存儲(chǔ)方式

在iOS開發(fā)過程中较剃,不管是做什么應(yīng)用上渴,都會(huì)碰到數(shù)據(jù)保存的問題蝉稳。本地存儲(chǔ)對(duì)提高數(shù)據(jù)交互效率有著重大的意義破婆。本文總結(jié)一下數(shù)據(jù)存儲(chǔ)的幾種方式

在這之前我們需要先對(duì)應(yīng)用程序的文件目錄有所了解,請(qǐng)看下圖

應(yīng)用程序的文件目錄

?應(yīng)用文件目錄
  • Document :保存應(yīng)用運(yùn)行時(shí)生成的需要持久化的數(shù)據(jù)啄巧,iTunes同步設(shè)備時(shí)會(huì)備份該目錄寻歧。例如,游戲應(yīng)用可將游戲存檔保存在該目錄

  • Library/Caches :保存應(yīng)用運(yùn)行時(shí)生成的需要持久化的數(shù)據(jù)秩仆,iTunes同步設(shè)備時(shí)不會(huì)備份該目錄码泛。一般存儲(chǔ)體積大、不需要備份的非重要數(shù)據(jù)

  • Library/Preference: 保存應(yīng)用的所有偏好設(shè)置澄耍,iOS的Settings(設(shè)置)應(yīng)用會(huì)在該目錄中查找應(yīng)用的設(shè)置信息噪珊。iTunes同步設(shè)備時(shí)會(huì)備份該目錄

  • temp :保存應(yīng)用運(yùn)行時(shí)所需的臨時(shí)數(shù)據(jù)晌缘,使用完畢后再將相應(yīng)的文件從該目錄刪除。應(yīng)用沒有運(yùn)行時(shí)痢站,系統(tǒng)也可能會(huì)清除該目錄下的文件磷箕。iTunes同步設(shè)備時(shí)不會(huì)備份該目錄

總結(jié):

為了避免同步過程時(shí)間過長(zhǎng),你需要對(duì)應(yīng)用中使用的文件放在哪里做出選擇阵难。很大的數(shù)據(jù)文件岳枷,盡量放置在Caches目錄下,而不是Documents目錄下呜叫,Documents目錄下文件將做備份空繁,這樣會(huì)很耗時(shí)。


存儲(chǔ)方式介紹

  1. NSKeyedArchiver: 采用歸檔的形式來保存數(shù)據(jù)沙盒中怀偷;
  2. NSUserDefaults:偏好設(shè)置數(shù)據(jù)存到沙盒的Library/Preferences目錄(本質(zhì)是plist);
  3. Write寫入方式: 永久保存在磁盤中;
  4. SQLite :采用SQLite數(shù)據(jù)庫來存儲(chǔ)數(shù)據(jù)家厌。

1.NSKeyedArchiver:(歸檔)

采用歸檔的形式來保存數(shù)據(jù),該數(shù)據(jù)對(duì)象需要遵守NSCoding協(xié)議椎工,并且該對(duì)象對(duì)應(yīng)的類必須提供encodeWithCoder:和initWithCoder:方法饭于。
前一個(gè)方法告訴系統(tǒng)怎么對(duì)對(duì)象進(jìn)行編碼,而后一個(gè)方法則是告訴系統(tǒng)怎么對(duì)對(duì)象進(jìn)行解碼维蒙。

缺點(diǎn):

歸檔的形式來保存數(shù)據(jù)掰吕,只能一次性歸檔保存以及一次性解壓。所以只能針對(duì)小量數(shù)據(jù)颅痊,而且對(duì)數(shù)據(jù)操作比較笨拙殖熟,即如果想改動(dòng)數(shù)據(jù)的某一小部分,還是需要解壓整個(gè)數(shù)據(jù)或者歸檔整個(gè)數(shù)據(jù)斑响。
例如對(duì)Person對(duì)象歸檔保存菱属。
定義Person:

@interface Person:NSObject{//遵守NSCoding協(xié)議
 NSString *name;//待歸檔類型
}
@implementation Person
-(void)encodeWithCoder:(NSCoder *)aCoder
{
 [aCoder encodeObject:name forKey:@"name"];
}
-(void)initWithCoder:(NSCoder *)aDecoder
{
 name=[aDeCoder decodeObjectforKey:@"name"];
}

歸檔操作:
如果對(duì)Person對(duì)象name屬性歸檔保存,只需要NSCoder子類NSKeyedArchiver的方法archiveRootObject:toFile: 即可舰罚。

    // 創(chuàng)建person對(duì)象
    Person *person = [[Person alloc] init];
    person.name = @"DNS";
    // 獲取cache
    NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
    // 拼接文件全路徑
    NSString *filePath = [cachePath stringByAppendingPathComponent:@"person.data"];
    // 把自定義對(duì)象歸檔
    [NSKeyedArchiver archiveRootObject:person toFile:filePath];

解檔操作:
同樣調(diào)用NSCoder子類NSKeyedArchiver的方法unarchiveRootObject:toFile: 即可

    NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
    NSString *filePath = [cachePath stringByAppendingPathComponent:@"person.data"];
    // 解檔
    [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
注: initWithCoder什么時(shí)候需要調(diào)用[super initWithCoder:]
? initWithCoder原理:只要解析文件就會(huì)調(diào)用纽门,xib,storyboard都是文件,因此只要解析這兩個(gè)文件营罢,就會(huì)調(diào)用initWithCoder赏陵。
? 因此如果在storyboard使用自定義view,重寫initWithCoder方法,一定要調(diào)用[super initWithCoder:]饲漾,因?yàn)橹挥邢到y(tǒng)才知道怎么解析storyboard蝙搔,如果沒有調(diào)用,就解析不了這個(gè)文件考传。

2.NSUserDefaults:(偏好設(shè)置吃型,本質(zhì)是plist)

用來保存應(yīng)用程序設(shè)置和屬性、用戶保存的數(shù)據(jù)伙菊。用戶再次打開程序或開機(jī)后這些數(shù)據(jù)仍然存在败玉。
NSUserDefaults可以存儲(chǔ)的數(shù)據(jù)類型包括:NSData敌土、NSString、NSNumber运翼、NSDate返干、NSArray、NSDictionary血淌。如果要存儲(chǔ)其他類型矩欠,則需要轉(zhuǎn)換為前面的類型,才能用NSUserDefaults存儲(chǔ)悠夯。

  • 好處:1.不需要關(guān)心文件名
    2.快速做鍵值對(duì)存儲(chǔ)

  • 壞處: 能及時(shí)存儲(chǔ)癌淮,需要做同步操作,把內(nèi)存中的數(shù)據(jù)同步到硬盤上

  • 底層:就是封裝了一個(gè)字典

注:在iOS7之前沦补,默認(rèn)不會(huì)馬上跟硬盤同步

存儲(chǔ):

    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];

    [userDefaults setObject:@"DNS" forKey:@"name"];
    // 在iOS7之前乳蓄,默認(rèn)不會(huì)馬上把跟硬盤同步
    // 同步操作
    [userDefaults synchronize];

讀取:

    NSString *name = [[NSUserDefaults standardUserDefaults] objectForKey:@"name"];
    NSLog(@"%@",name);

3. Write寫入方式:永久保存在磁盤中。

具體方法為:
第一步:獲得文件即將保存的路徑:

//使用C函數(shù)NSSearchPathForDirectoriesInDomains來獲得沙盒中目錄的全路徑夕膀。
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
//該函數(shù)有三個(gè)參數(shù)虚倒,目錄類型、he domain mask产舞、布爾值魂奥。
//其中布爾值表示是否需要通過~擴(kuò)展路徑。而且第一個(gè)參數(shù)是不變的易猫,即為NSSearchPathDirectory 耻煤。
//在IOS中后兩個(gè)參數(shù)也是不變的,即為:NSUserDomainMask 和 YES准颓。
NSString *ourDocumentPath =[documentPaths objectAtIndex:0];


//還有一種方法是使用NSHomeDirectory函數(shù)獲得sandbox的路徑哈蝇。
NSString *sandboxPath = NSHomeDirectory();
//將Documents添加到sandbox路徑上
NSString *documentPath = [sandboxPath stringByAppendingPathComponent:@"Documents"];


//這兩者的區(qū)別就是:使用NSSearchPathForDirectoriesInDomains比在NSHomeDirectory后面添加Document更加安全。因?yàn)樵撐募夸浛赡茉谖磥戆l(fā)送的系統(tǒng)上發(fā)生改變攘已。

第二步:生成在該路徑下的文件:

//fileName就是保存文件的文件名
NSString *FileName=[documentDirectory stringByAppendingPathComponent:fileName];

第三步:往文件中寫入數(shù)據(jù):

//將NSData類型對(duì)象data寫入文件买鸽,文件名為FileName
[data writeToFile:FileName atomically:YES];
//最后從文件中讀出數(shù)據(jù):
NSData data=[NSData dataWithContentsOfFile:FileName options:0 error:NULL];

4. SQLite:采用數(shù)據(jù)庫來存儲(chǔ)數(shù)據(jù)。

SQLite (http://www.sqlite.org/docs.html) 是一個(gè)輕量級(jí)的關(guān)系數(shù)據(jù)庫贯被。iOS SDK很早就支持了SQLite,在使用時(shí)妆艘,只需要加入 libsqlite3.dylib 依賴以及引入 sqlite3.h 頭文件即可彤灶。但是,原生的SQLite API在使用上相當(dāng)不友好批旺,在使用時(shí)幌陕,非常不便。于是汽煮,開源社區(qū)中就出現(xiàn)了一系列將SQLite API進(jìn)行封裝的庫搏熄,而FMDB (https://github.com/ccgus/fmdb) 則是開源社區(qū)中的優(yōu)秀者棚唆。

  1. 什么是FMDB
    FMDB是iOS平臺(tái)的SQLite數(shù)據(jù)庫框架
    FMDB以O(shè)C的方式封裝了SQLite的C語言API

  2. FMDB的優(yōu)點(diǎn)
    使用起來更加面向?qū)ο螅∪チ撕芏嗦闊┬睦⑷哂嗟腃語言代碼
    對(duì)比蘋果自帶的Core Data框架宵凌,更加輕量級(jí)和靈活
    提供了多線程安全的數(shù)據(jù)庫操作方法,有效地防止數(shù)據(jù)混亂

  3. FMDB的github地址
    https://github.com/ccgus/fmdb

二止后、核心類

FMDB有三個(gè)主要的類

  1. FMDatabase
    一個(gè)FMDatabase對(duì)象就代表一個(gè)單獨(dú)的SQLite數(shù)據(jù)庫
    用來執(zhí)行SQL語句

  2. FMResultSet
    使用FMDatabase執(zhí)行查詢后的結(jié)果集

  3. FMDatabaseQueue
    用于在多線程中執(zhí)行多個(gè)查詢或更新瞎惫,它是線程安全的

三、打開數(shù)據(jù)庫

通過指定SQLite數(shù)據(jù)庫文件路徑來創(chuàng)建FMDatabase對(duì)象

//獲得數(shù)據(jù)庫文件的路徑 NSString *doc=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *fileName=[doc stringByAppendingPathComponent:@"student.sqlite"];
 FMDatabase *db = [FMDatabase databaseWithPath:path];
if (![db open]) {
 NSLog(@"數(shù)據(jù)庫打開失斠胫辍瓜喇!");
}

文件路徑有三種情況

  1. 具體文件路徑
      如果不存在會(huì)自動(dòng)創(chuàng)建

  2. 空字符串@""
      會(huì)在臨時(shí)目錄創(chuàng)建一個(gè)空的數(shù)據(jù)庫
      當(dāng)FMDatabase連接關(guān)閉時(shí),數(shù)據(jù)庫文件也被刪除

  3. nil
      會(huì)創(chuàng)建一個(gè)內(nèi)存中臨時(shí)數(shù)據(jù)庫歉糜,當(dāng)FMDatabase連接關(guān)閉時(shí)乘寒,數(shù)據(jù)庫會(huì)被銷毀

四、執(zhí)行更新

在FMDB中匪补,除查詢以外的所有操作伞辛,都稱為“更新”
create、drop叉袍、insert始锚、update、delete等

使用executeUpdate:方法執(zhí)行更新
- (BOOL)executeUpdate:(NSString*)sql, ...
- (BOOL)executeUpdateWithFormat:(NSString*)format, ...
- (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments

示例
[db executeUpdate:@"UPDATE t_student SET age = ? WHERE name = ?;", @20, @"Jack"]
五喳逛、執(zhí)行查詢
查詢方法
- (FMResultSet *)executeQuery:(NSString*)sql, ...
- (FMResultSet *)executeQueryWithFormat:(NSString*)format, ...
- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments

示例
// 查詢數(shù)據(jù)
FMResultSet *rs = [db executeQuery:@"SELECT * FROM t_student"];

// 遍歷結(jié)果集
while ([rs next]) {
 NSString *name = [rs stringForColumn:@"name"];
 int age = [rs intForColumn:@"age"];
 double score = [rs doubleForColumn:@"score"];
}
使用FMDatabaseQueue

線程安全:在多個(gè)線程中同時(shí)使用一個(gè)FMDatabase實(shí)例是不明智的∏瓢疲現(xiàn)在你可以為每個(gè)線程創(chuàng)建一個(gè)FMDatabase對(duì)象。 不要讓多個(gè)線程分享同一個(gè)實(shí)例润文,它無法在多個(gè)線程中同時(shí)使用姐呐。 若此,程序會(huì)時(shí)不時(shí)崩潰典蝌,或者報(bào)告異常,讓人崩潰曙砂。所以,不要初始化FMDatabase對(duì)象骏掀,然后在多個(gè)線程中使用鸠澈。

請(qǐng)使用 FMDatabaseQueue。以下是使用方法:

//首先創(chuàng)建隊(duì)列截驮。
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];
//這樣使用笑陈。
[queue inDatabase:^(FMDatabase *db) {
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]];
FMResultSet *rs = [db executeQuery:@"select * from foo"];

while([rs next]) { … }}];
//像這樣,輕松地把簡(jiǎn)單任務(wù)包裝到事務(wù)里:
[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]];
[db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]];

if (whoopsSomethingWrongHappened) { *rollback = YES; return; }

// etc… [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:4]]; }];

FMDatabaseQueue 后臺(tái)會(huì)建立系列化的G-C-D隊(duì)列葵袭,并執(zhí)行你傳給G-C-D隊(duì)列的塊涵妥。這意味著 你從多線程同時(shí)調(diào)用調(diào)用方法,GDC也會(huì)按它接收的塊的順序來執(zhí)行坡锡。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蓬网,一起剝皮案震驚了整個(gè)濱河市窒所,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌帆锋,老刑警劉巖吵取,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異窟坐,居然都是意外死亡海渊,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門哲鸳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來臣疑,“玉大人,你說我怎么就攤上這事徙菠⊙渡颍” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵婿奔,是天一觀的道長(zhǎng)缺狠。 經(jīng)常有香客問我,道長(zhǎng)萍摊,這世上最難降的妖魔是什么挤茄? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮冰木,結(jié)果婚禮上穷劈,老公的妹妹穿的比我還像新娘。我一直安慰自己踊沸,他們只是感情好歇终,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著逼龟,像睡著了一般评凝。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上腺律,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天奕短,我揣著相機(jī)與錄音,去河邊找鬼匀钧。 笑死篡诽,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的榴捡。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼朱浴,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼吊圾!你這毒婦竟也來了达椰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤项乒,失蹤者是張志新(化名)和其女友劉穎啰劲,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體檀何,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蝇裤,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了频鉴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片栓辜。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖垛孔,靈堂內(nèi)的尸體忽然破棺而出藕甩,到底是詐尸還是另有隱情,我是刑警寧澤周荐,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布狭莱,位于F島的核電站,受9級(jí)特大地震影響概作,放射性物質(zhì)發(fā)生泄漏腋妙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一讯榕、第九天 我趴在偏房一處隱蔽的房頂上張望骤素。 院中可真熱鬧,春花似錦瘩扼、人聲如沸谆甜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽规辱。三九已至,卻和暖如春栽燕,著一層夾襖步出監(jiān)牢的瞬間罕袋,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國打工碍岔, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留浴讯,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓蔼啦,卻偏偏與公主長(zhǎng)得像榆纽,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

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