簡介
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個基本屬性
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];