數(shù)據(jù)持久化之Core Data

簡介

Core Data是一個功能強(qiáng)大的對象數(shù)據(jù)庫觅彰,提供了強(qiáng)大的數(shù)據(jù)存儲管理功能。它提供的對象-關(guān)系映射功能替裆,能夠?qū)c對象轉(zhuǎn)換成數(shù)據(jù)校辩,保存在SQLite數(shù)據(jù)庫中,也能夠?qū)⒈4嬖跀?shù)據(jù)庫中的數(shù)據(jù)還原成oc對象扎唾。在此數(shù)據(jù)操作期間召川,我們不需要編寫任何SQL語句。

下邊是Core Data提供的一些功能:

  • 使用可視化模型編輯器進(jìn)行數(shù)據(jù)對象建模胸遇。
  • 處理對象架構(gòu)(schema)變更的自動化和手動遷移工具。
  • 在對象之間建立一對一汉形、一對多和多對多關(guān)系纸镊。
  • 以不同的格式將數(shù)據(jù)存儲到獨(dú)立的文件中。
  • 對象特性(attribute)驗(yàn)證概疆。
  • 數(shù)據(jù)查詢和排序逗威。
  • 延遲加載數(shù)據(jù)。
  • 與iOS表視圖緊密協(xié)作岔冀。
  • 使用提交和撤銷功能管理相關(guān)的對象變更凯旭。
    本文主要介紹在應(yīng)用中集成和利用Core Data需要的組件以及Core Data的基本功能,幫助判斷在應(yīng)用中使用Core Data是否是合適的選擇。

判斷是否適合使用Core Data

要使用持久化數(shù)據(jù)罐呼,iOS開發(fā)可以選擇:

  • NSUserDefaults:這種方式通常用于保存應(yīng)用首選項(xiàng)鞠柄。NSUserDefaults很像使用鍵值存儲的NSDictionary,可以存儲用NSArray、 NSDictionary嫉柴、NSData厌杜、NSDate、NSString计螺、NSNumber表示的值夯尽。如果使用鍵值對、字典或數(shù)組可以滿足需求登馒,就可使用NSUserDefaults匙握。
  • 屬性列表(plist):NSArray和NSDictionary都支持讀寫用戶定義的屬性列表文件,這種文件采用XML格式陈轿,可以存儲NSNumber肺孤、NSString、NSDate济欢、NSData赠堵、NSDictionary、NSArray法褥。如果使用字典或數(shù)組可滿足持久化需求茫叭,就可以使用屬性列表文件。
  • 編碼器(Coder)和鍵式歸檔(Key Archive):NSCoder和NSKeyedArchiver支持將任何圖片存儲到二進(jìn)制文件中半等。要使用這種持久化方式揍愁,要對存儲的每個自定義對象都必須實(shí)現(xiàn)NSCoder定義的方法,而開發(fā)人員必須負(fù)責(zé)保存和加載杀饵。如果只需要幾個自定義對象就能滿足的持久化需求莽囤,就可以使用這種方式。
  • 直接使用SQLite:在iOS應(yīng)用中切距,使用SQLite可以實(shí)現(xiàn)SQL支持的任何數(shù)據(jù)持久化邏輯朽缎,如定義數(shù)據(jù)庫中表和關(guān)系、插入數(shù)據(jù)谜悟、查詢數(shù)據(jù)以及更新和刪除數(shù)據(jù)话肖。這種方式的缺點(diǎn)是,應(yīng)用需要在對象和SQL文件之間建立映射葡幸,需要編寫檢索和保存數(shù)據(jù)的SQL查詢最筒,還需要寫代碼來跟蹤需要保存的對象。
  • Core Data:提供了直接使用SQLite的大部分靈活性蔚叨,同時應(yīng)用無需關(guān)系數(shù)據(jù)庫使用機(jī)制如果應(yīng)用需要的數(shù)據(jù)很多床蜘、需要維護(hù)對象之間的關(guān)系或者需要快速而輕松的訪問特定的對象辙培,Core Data是不錯的選擇。

Core Data的4個重要的類

  • ** NSManagedObjectModel**:被管理的數(shù)據(jù)模型邢锯,相當(dāng)于數(shù)據(jù)庫中的一條記錄扬蕊。每一個NSManagedObject對象,都有一個全局 ID(類型為:NSManagedObjectID)弹囚。每個在NSManagedObjectContext注冊過的NSManagedObject厨相,可以通過這個全局 ID 在上下文中查詢到。每個在持久存儲層中的對象鸥鹉,都對應(yīng)一個與上下文NSManagedObject
  • NSPersistentStore:持久存儲區(qū)蛮穿,表示存儲數(shù)據(jù)的文件。配置應(yīng)用使用Core Data時毁渗,只需要指定持久存儲區(qū)的名稱践磅,位置和類型。在iOS中灸异,Core Data可以使用的存儲類型包括SQLite和二進(jìn)制文件府适。大多數(shù)iOS應(yīng)用都使用SQLite存儲區(qū),因?yàn)檫@讓Core Data能夠利用查詢功能肺樟,改善性能檐春。需要注意的一個重點(diǎn)是,Core Data直接管理SQLite文件么伯。Core Data不能使用既有的SQLite文件疟暖,而必須是創(chuàng)建文件和架構(gòu),這是Core Data首次使用不存在的文件時完成的田柔。
    將持久存儲區(qū)加入之久存儲協(xié)調(diào)器后俐巴,就可以創(chuàng)建托管對象上下文以并托管對象交互了。
  • NSPersistentStoreCoordinator:持久存儲協(xié)調(diào)器是中間人硬爆,位于存儲對象的實(shí)際文件和應(yīng)用與之交互的對象模型之間欣舵。除了在搭建Core Data環(huán)境時實(shí)例化存儲協(xié)調(diào)器外,應(yīng)用不與它做其他交互缀磕。實(shí)例化后缘圈,需要將持久存儲區(qū)加入其中,這樣才能訪問數(shù)據(jù)虐骑。
  • NSManagedObjectContext:托管對象上下文准验,是包含托管對象的工作區(qū)。要創(chuàng)建廷没、刪除或查詢對象,應(yīng)用必須與托管對象上下文交互垂寥。另外颠黎,托管對象上下文還能夠管理相關(guān)的變更另锋。例如,應(yīng)用可插入狭归,更新和刪除一些對象,再一次性保存所有修改,甚至撤銷這些修改物赶。

上邊是關(guān)系模型荸频,即數(shù)據(jù)庫,數(shù)據(jù)庫里有張person表疚宇,person表里有id亡鼠、name、age三個字段敷待,而且有兩條記錄间涵;
下邊是對象模型,可以看到有2個oc對象榜揖;
利用Core Data框架勾哩,我們可以輕松的將數(shù)據(jù)庫里面的2條記錄轉(zhuǎn)換成2個oc對象,也可以輕松的將2個oc對象保存到數(shù)據(jù)庫中举哟,變成2條記錄思劳,而且不用寫一句SQL語句。

模型文件

在Core Data需要映射的行為對象稱為:實(shí)體(entity)妨猩,而且需要使用Core Data的模型文件來描述app中的所有實(shí)體和實(shí)體屬性潜叛。這里以person和card2個實(shí)體為例,先看看實(shí)體屬性和實(shí)體之間的關(guān)聯(lián)關(guān)系:

Person Card
name no
age person
card
  • Person中有個card屬性册赛,Card中有個person屬性
    -屬于一對一雙向關(guān)聯(lián)
    Person實(shí)體中有:name(姓名)钠导、age(年齡)、card(身份證)三個屬性
    Card實(shí)體中有:no(號碼)森瘪、person(人)兩個屬性

接下來看看創(chuàng)建模型文件的過程:
1牡属、選擇模版

2、添加實(shí)體

3扼睬、添加Person的2個基本屬性

4逮栅、添加Card的1個基本屬性

5D02948C-A200-45B0-95E0-316F245C7D50.png

5、建立Card和Person的關(guān)聯(lián)關(guān)系

了解NSManagedObject

1窗宇、通過Core Data從數(shù)據(jù)庫取出的對象措伐,默認(rèn)情況下都是NSManagedObject對象
2、NSManagedObject的工作模式有點(diǎn)類似于NSDictionary對象军俊,通過鍵-值對來存取所有的實(shí)體屬性
1>setValue:forKey:存儲屬性值(屬性名為key)
2>valueForKey:獲取屬性值(屬性名為key)

Core Data中的核心對象


注:白色表示類名侥加,紅色表示類的一個屬性
開發(fā)步驟:
1、初始化NSManagedObjectModel對象粪躬,加載模型文件担败,讀取app中的所有實(shí)體信息
2昔穴、初始化NSPersistentStoreCoordinator對象,添加持久庫(這里采用SQLite數(shù)據(jù)庫)
3提前、初始化NSManagedObjectContext對象吗货,拿到這個上下文對象操作實(shí)體,進(jìn)行CRUD操作

代碼實(shí)現(xiàn)

先添加CoreData.framework和導(dǎo)入<CoreData/CoreData.h>頭文件



1狈网、搭建上下文環(huán)境

 // 從應(yīng)用程序包中加載模型文件
    NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
    // 傳入模型對象宙搬,初始化NSPersistentStoreCoordinator
    NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
    // 構(gòu)建SQLite數(shù)據(jù)庫文件的路徑
    NSString *dos = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSURL *url = [NSURL fileURLWithPath:[dos stringByAppendingString:@"person.data"]];
    // 添加持久化存儲庫,這里使用SQLite作為存儲庫
    NSError *error = nil;
    NSPersistentStore *store = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:&error];
    if (store == nil) {
        [NSException raise:@"添加數(shù)據(jù)庫錯誤" format:@"%@",error.localizedDescription];
    }
    // 初始化上下文拓哺,設(shè)置persisterStoreCoordinator屬性
    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    context.persistentStoreCoordinator = psc;

2勇垛、添加數(shù)據(jù)到數(shù)據(jù)庫

// 傳入上下文,創(chuàng)建person的實(shí)體對象
    NSManagedObject *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:context];
    // 設(shè)置person的屬性
    [person setValue:@"道道" forKey:@"name"];
    [person setValue:[NSNumber numberWithInt:18] forKey:@"age"];
    // 傳入上下文拓售,創(chuàng)建一個Card的實(shí)體對象
    NSManagedObject *card = [NSEntityDescription insertNewObjectForEntityForName:@"Card" inManagedObjectContext:context];
    [card setValue:@"20170518" forKey:@"no"];
    // 設(shè)置person和card之間的關(guān)聯(lián)關(guān)系
    [person setValue:card forKey:@"card"];
    // 利用上下文對象窥摄,將數(shù)據(jù)同步到持久化存儲庫中
    NSError *error = nil;
    BOOL isSuccess = [context save:&error];
    if (!isSuccess) {
        [NSException raise:@"訪問數(shù)據(jù)庫錯誤" format:@"%@",error.localizedDescription];
    }
    // 如果是想做更新操作:只要在更改了實(shí)體對象的屬性后調(diào)用[context save:&error],就能將更改的數(shù)據(jù)同步到數(shù)據(jù)庫

3.從數(shù)據(jù)庫中查詢數(shù)據(jù)

 // 初始化一個查詢請求
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    // 設(shè)置查詢實(shí)體
    request.entity = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:context];
    // 設(shè)置排序(按照age降序)
    NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:NO];
    request.sortDescriptors = [NSArray arrayWithObject:sort];
    // 設(shè)置條件過濾(搜索name中包含“dao”的紀(jì)錄,注意:設(shè)置條件過濾時础淤,數(shù)據(jù)庫SQL語句中的%要用*來代替崭放,所以%dao應(yīng)該寫成*dao*)
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name like %@",@"*dao*"];
    request.predicate = predicate;
    // 執(zhí)行請求
    NSError *error = nil;
    NSArray *objs = [context executeFetchRequest:request error:&error];
    if (error) {
        [NSException raise:@"查詢出錯" format:@"%@",error.localizedDescription];
    }
    // 遍歷數(shù)據(jù)
    for (NSManagedObject *obj in objs) {
        NSLog(@"name = %@\n",[obj valueForKey:@"name"]);
    }

注Core Data不會根據(jù)實(shí)體中的關(guān)聯(lián)關(guān)系立即獲取相應(yīng)的關(guān)聯(lián)對象,比如通過Core Data取出Person實(shí)體時鸽凶,并不會立即查詢到相關(guān)的Card實(shí)體币砂;當(dāng)應(yīng)用真的需要Card時,才會再次查詢數(shù)據(jù)庫玻侥,加載Card實(shí)體的信息决摧。這就是Core Data的延遲加載機(jī)制

4.刪除數(shù)據(jù)庫中的數(shù)據(jù)

// 傳入需要刪除的實(shí)體對象
    [context deleteObject:managedObject];
    // 將結(jié)果同步到數(shù)據(jù)庫
    NSError *error = nil;
    [context save:&error];
    if (error) {
        [NSException raise:@"刪除出錯" format:@"%@",error.description];
    }

打開Core Data的SQL語句輸出開關(guān)

1.打開Product,點(diǎn)擊EditScheme
2.點(diǎn)擊Arguments凑兰,在ArgumentsPassed On Launch中添加2項(xiàng)
1> -com.apple.CoreData.SQLDebug
2> 1

創(chuàng)建NSManagedObject的子類

默認(rèn)情況下掌桩,利用Core Data取出的實(shí)體都是NSManagedObject類型的,能夠利用鍵-值隊(duì)來存取數(shù)據(jù)姑食。但是一般情況下波岛,實(shí)體在存取數(shù)據(jù)的基礎(chǔ)上,有時還需要添加一些業(yè)務(wù)方法來完成一些其他任務(wù)音半,那么就必須創(chuàng)建NSManagedObject的子類


將對應(yīng)的頭文件導(dǎo)入后则拷,往數(shù)據(jù)庫里新增數(shù)據(jù)就可以這樣寫了:

Person *person1 = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:context];
    person1.name = @"sundaodao";
    person1.age = 20;
    
    Card *card1 = [NSEntityDescription insertNewObjectForEntityForName:@"Card" inManagedObjectContext:context];
    card1.no = @"20170502";
    person1.card = card1;
// 最后調(diào)用[context save&error];保存數(shù)據(jù) 

講到這里,整個Core Data框架的入門就結(jié)束了曹鸠,其實(shí)Core Data還遠(yuǎn)不止這些功能煌茬,它還支持自動撤銷機(jī)制,一對多關(guān)聯(lián)等彻桃。

Core Data版本遷移基礎(chǔ)

一般app升級時坛善,數(shù)據(jù)庫很有可能發(fā)生改變,如新增表字段,增加表等浑吟。此時有兩種處理方法:
1.把本地舊數(shù)據(jù)庫直接刪除笙纤,重新建立子新的數(shù)據(jù)庫
2.數(shù)據(jù)庫遷移耗溜,更新數(shù)據(jù)庫
第一種處理簡單粗暴组力,但不會保留任何歷史數(shù)據(jù),一般不推薦使用抖拴,這里主要介紹第二種數(shù)據(jù)遷移燎字。
首先選中Core Data文件,然后選擇Editor -> Add Model Version...阿宅,如圖所示:



接下來我們將Core Data文件切換到新生成的TEstModel-v2.xcddatamodel候衍,如下圖所示:


現(xiàn)在就可以在新的文件中修改數(shù)據(jù)了,此外還需要修改部分代碼:

// 數(shù)據(jù)庫版本遷移新增代碼
NSDictionary *options = @{NSMigratePersistentStoresAutomaticallyOption:[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption:[NSNumber numberWithBool:YES]};
NSPersistentStore *store = [_psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:options error:&error];
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末洒放,一起剝皮案震驚了整個濱河市蛉鹿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌往湿,老刑警劉巖妖异,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異领追,居然都是意外死亡他膳,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進(jìn)店門绒窑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來棕孙,“玉大人,你說我怎么就攤上這事些膨◇翱。” “怎么了?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵订雾,是天一觀的道長肢预。 經(jīng)常有香客問我,道長葬燎,這世上最難降的妖魔是什么误甚? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮谱净,結(jié)果婚禮上窑邦,老公的妹妹穿的比我還像新娘。我一直安慰自己壕探,他們只是感情好冈钦,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著李请,像睡著了一般瞧筛。 火紅的嫁衣襯著肌膚如雪厉熟。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天较幌,我揣著相機(jī)與錄音揍瑟,去河邊找鬼。 笑死乍炉,一個胖子當(dāng)著我的面吹牛绢片,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播岛琼,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼底循,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了槐瑞?” 一聲冷哼從身側(cè)響起熙涤,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎困檩,沒想到半個月后祠挫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡窗看,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年茸歧,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片显沈。...
    茶點(diǎn)故事閱讀 38,626評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡软瞎,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拉讯,到底是詐尸還是另有隱情涤浇,我是刑警寧澤,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布魔慷,位于F島的核電站只锭,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏院尔。R本人自食惡果不足惜蜻展,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望邀摆。 院中可真熱鬧纵顾,春花似錦、人聲如沸栋盹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至汉额,卻和暖如春曹仗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蠕搜。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工怎茫, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人讥脐。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓遭居,卻偏偏與公主長得像,于是被迫代替她去往敵國和親旬渠。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評論 2 348

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

  • 一.可以將oc對象轉(zhuǎn)換成數(shù)據(jù)保存到sqlite數(shù)據(jù)庫中端壳,也可以將數(shù)據(jù)庫數(shù)據(jù)還原成oc對象coredata用的對象結(jié)...
    wangyu2488閱讀 201評論 0 0
  • 應(yīng)用沙盒 每個iOS應(yīng)用都有自己的應(yīng)用沙盒(應(yīng)用沙盒就是文件系統(tǒng)目錄)告丢,與其他文件系統(tǒng)隔離。應(yīng)用必須待在自己的沙盒...
    iOS_程序猿閱讀 1,103評論 0 6
  • 1.簡介 數(shù)據(jù)持久存儲是一種非易失性存儲损谦,在重啟動計算機(jī)或設(shè)備后也不會丟失數(shù)據(jù)岖免。持久化技術(shù)主要用于MVC模型中的m...
    公子無禮閱讀 1,674評論 0 4
  • 最近通往財富自由之路上有越來越多的關(guān)于投資的建議,以及計算公式照捡,很誠懇的說颅湘,到現(xiàn)在我也沒有具體查過這些公式,那三個...
    ToBeMself閱讀 160評論 0 1
  • 說不出的言語我只愿意這兩個字栗精,“真好”有點(diǎn)欲說還休闯参,傻乎乎的滿足感。 今天南京又降小雨悲立,毛毛躁躁的雨鹿寨,癢酥酥的撓的...
    綠岸閱讀 467評論 0 2