一栅贴、簡介
Core Data 是蘋果官方提供的管理數(shù)據(jù)層對象的框架盾鳞,它提供了對象—關(guān)系映射(ORM)的功能瞒御,即能夠?qū)?Objective-C 對象轉(zhuǎn)化成數(shù)據(jù),保存在 SQLite 數(shù)據(jù)庫文件中徙垫,也能夠?qū)⒈4嬖跀?shù)據(jù)庫中的數(shù)據(jù)還原成 Objective-C 對象。在此數(shù)據(jù)操作期間放棒,不需要編寫任何 SQL 語句姻报。Core Data 針對對象生命周期,以及持久化的對象圖管理(object graph management)中的一些常見問題提供了解決方案间螟。CoreData 的主要任務(wù)是負(fù)責(zé)數(shù)據(jù)更改的管理吴旋、序列化到磁盤、最小化內(nèi)存占用以及查詢數(shù)據(jù)厢破。
Core Data 比 SQLite 做了更進一步的封裝荣瑟,SQLite 提供了數(shù)據(jù)的存儲模型,并提供了一系列 API溉奕,你可以通過 API 讀寫數(shù)據(jù)庫褂傀,去處理想要處理的數(shù)據(jù)。但是 SQLite 存儲的數(shù)據(jù)和你編寫代碼中的數(shù)據(jù)(比如一個類的對象)并沒有內(nèi)置的聯(lián)系加勤,必須你自己編寫代碼去一一對應(yīng)仙辟。
而 Core Data 卻可以解決一個數(shù)據(jù)在持久化層和代碼層的一一對應(yīng)關(guān)系同波。也就是說,你處理一個對象的數(shù)據(jù)后叠国,通過保存接口未檩,它可以自動同步到持久化層里,而不需要你去實現(xiàn)額外的代碼粟焊。
iOS10 中利用 NSPersistentContainer
iOS10 之前涉及 NSManagedObjectContext
冤狡、NSPersistentStoreCoordinator
逻卖、NSManagedObjectModel
泵喘、NSPersistentStore
這些類。
1.Core Data Stack
說是一個對象的集合缀程,由4個主要對象構(gòu)成:
- managed object context (NSManagedObjectContext)
- persistent store coordinator (NSPersistentStoreCoordinator)
- managed object model (NSManagedObjectModel)
- persistent container (NSPersistentContainer)
Core Data Stack香追,就是進行數(shù)據(jù)增刪查改合瓢、保存的工作臺,Apple 提供這樣一個工作臺透典,讓你方便進行數(shù)據(jù)的保存晴楔。無需關(guān)心實現(xiàn)細(xì)節(jié)。
2.Persistent Container
NSPersistentContainer
是iOS 10峭咒、 macOS 10.12 之后才出現(xiàn)的新類税弃。引入這個新類的目的之一,就是為了簡化創(chuàng)建 Core Data Stack 這個工作臺的過程凑队。所以则果,在 iOS10 之前,創(chuàng)建 Core Data Stack 會復(fù)雜一些顽决。
而 Persistent Container 也有另一個新類 NSPersistentStoreDescription
短条,可以利用這個類,進行一些定制化設(shè)置才菠,比如自定義存儲路徑茸时、設(shè)置存儲數(shù)據(jù)方式等(Core Data 支持 SQLite、XML赋访、Binary可都、InMemory 4中方式存儲數(shù)據(jù))。
備注:iOS10中蚓耽,如果利用 NSPersistentContainer
創(chuàng)建 Core Data Stack渠牲,預(yù)設(shè)的是 NSSQLiteStoreType
類型。并且默認(rèn)打開了自動輕量化版本遷移功能(換言之步悠,在 iOS10 之前签杈,需要手動進行相關(guān)設(shè)置,才能打開版本遷移功能)。
3.Managed Object Context答姥。
可以理解為是一塊內(nèi)存铣除,提供了和 Managed Objects 交互的場所。也稱為:The Context 或者 MOC鹦付。NSManagedObjectContext
類實例尚粘。
備注:對數(shù)據(jù)進行刪除、保存敲长、查詢郎嫁,都要用到 NSManagedObjectContext
類的相關(guān)方法。
4.Managed Object Model
直觀點祈噪,你可以把它理解為就是 Xcode 中后綴為 xcdatamodel
的文件泽铛。在這個文件里,你可以通過非代碼钳降、可視化的方式厚宰,定義對象腌巾、對象的屬性遂填、對象之間的關(guān)系(Core Data 把對象稱呼為實體、對象的屬性稱呼為特性)澈蝙。
Managed Object Model 就是 Core Data 中用于描述實體吓坚、實體特性、實體間關(guān)系的一套方案灯荧。
它是 NSManagenObjectModel
的類實例(也可以通過純代碼實現(xiàn) .xcdatamodel
文件的內(nèi)容)礁击。
Entity - 實體
NSEntityDescription
類實例,用于定義一個對象逗载。一個實體哆窿,最少要有名字和類名(如果沒有設(shè)置類名,默認(rèn)是 NSManagedObject
類)厉斟。
Attribute - 特性
實體特性 NSAttributeDescription
類實例挚躯。就是 Entity 的特性,對應(yīng) App 中的創(chuàng)建類時的屬性擦秽。
Relationship - 關(guān)系
實體關(guān)系 NSRelationshipDescription
類實例码荔。用于描述 Entity 之間的關(guān)系。
5.Managed Object感挥。
就是需要保存的數(shù)據(jù)缩搅,是 NSManagenObject
類實例。(對應(yīng) App 中的對象)
就我的理解触幼,Managed Object 和上面提到的 Entity硼瓣,本質(zhì)上是同一個東西,就是你的數(shù)據(jù)對象置谦,只不過是在可視化操作和純代碼操作中的不同稱謂堂鲤。
6.Persistent Store Coordinator
協(xié)調(diào) Context 和 Persistent Store 的一個角色噪猾。NSPersistentStoreCoordinator
類實例。
如果只是對數(shù)據(jù)進行簡單的增刪查改筑累,我們并不需要接觸到這個類袱蜡。
7.Persistent Store
可以理解為保存數(shù)據(jù)的地方。用于設(shè)置保存數(shù)據(jù)的方式慢宗、以及保存的路徑等坪蚁。(保存數(shù)據(jù)的方式指 SQLite、XML镜沽、Binary敏晤、InMemory 4種)。NSPersistentStore
類實例缅茉。也稱為 The Store 或者 Database嘴脾。
在 iOS10 之前,如果需要支持版本遷移功能蔬墩,需要在創(chuàng)建 NSPersistentStore
類實例時译打,傳入相應(yīng)的 options
參數(shù)。而在 iOS10 中拇颅,則會自動打開版本遷移功能奏司,并默認(rèn)設(shè)置數(shù)據(jù)類型為 NSSQLiteStoreType
。
版本遷移樟插,假如修改了數(shù)據(jù)模型(比如修改了 . xcdatamodel
文件:增加了實體,增加了特性等等?)韵洋,為了防止使用者在更新 App 后,由于數(shù)據(jù)模型不一致導(dǎo)致崩潰黄锤,需要進行一定的處理搪缨,這個處理叫版本遷移。
二鸵熟、使用
1.初始化 Core Data Stack
不過由于 iOS10 新引進了 NSPersistentContainer
類副编,然后新建項目又可以選擇勾選 Core Data 與否。所以情況變得稍稍有點復(fù)雜旅赢。
這里分三種情況:1齿桃、在既有項目(只需支持iOS10)初始化 Core Data Stack;2煮盼、在既有項目(需兼容iOS8短纵、9、10等系統(tǒng))初始化 Core Data Stack僵控;3香到、新建項目時直接勾選了 Core Data。
(1)在既有項目添加 Core Data 功能(只需支持iOS10)
// 我們先聲明了一個NSPersistentContainer類型的屬性:persistentContainer,在適合的時間調(diào)用initWithName:對其初始化
// 這里的Name參數(shù)悠就,需要和后續(xù)創(chuàng)建的.xcdatamodeld模型文件名稱一致千绪。
_persistentContainer = [[NSPersistentContainer alloc] initWithName:@"Model"];
// 調(diào)用loadPersistentStoresWithCompletionHandler:方法,完成Core Data Stack的最中初始化梗脾。
// 如果不能初始化成功荸型,在Block回調(diào)中打印錯誤,方便調(diào)試
[_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription * _Nonnull description, NSError * _Nullable error) {
if (error != nil) {
NSLog(@"Fail to load Core Data Stack : %@", error);
abort();
}
else {
...
}
}];
分為兩步:
- 初始化
NSPersistentContainer
對象 - 調(diào)用
NSPersistentContainer
的loadPersistentStoresWithCompletionHandler:
完成初始化
(2)在既有項目初始化 Core Data Stack(需兼容iOS8炸茧、9瑞妇、10等系統(tǒng))
- (instancetype)init
{
self = [super init];
if (self) {
NSInteger majorVersion = [NSProcessInfo processInfo].operatingSystemVersion.majorVersion;
if (majorVersion < 10) {
// iOS10以下的系統(tǒng), 用舊有的方法初始化Core Data Stack
[self initializeCoreDataLessThaniOS10];
}
else {
// iOS10的系統(tǒng), 用新的方法(詳見上面介紹的情況1)
[self initializeCoreData];
}
}
return self;
}
- (void)initializeCoreDataLessThaniOS10 {
// Get managed object model(拿到模型文件,也就是.xcdatamodeld文件(我們會在初始化完Core data Stack后創(chuàng)建))
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Model" withExtension:@"momd"];
NSManagedObjectModel *mom = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
NSAssert(mom != nil, @"Error initalizing Managed Object Model");
// Create persistent store coordinator(創(chuàng)建NSPersistentStoreCoordinator對象(需要傳入上述創(chuàng)建的NSManagedObjectModel對象))
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
// Creat managed object context(創(chuàng)建NSManagedObjectContext對象(_context是聲明在.h文件的屬性——因為其他類也要用到這個屬性))
_context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
// assgin persistent store coordinator(賦值persistentStoreCoordinator)
_context.persistentStoreCoordinator = psc;
// Create .sqlite file(在沙盒中創(chuàng)建.sqlite文件)
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *documentsURL = [[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *storeURL = [documentsURL URLByAppendingPathComponent:@"DataModel.sqlite"];
// Create persistent store(異步創(chuàng)建NSPersistentStore并add到NSPersistentStoreCoordinator對象中,作用是設(shè)置保存的數(shù)據(jù)類型(NSSQLiteStoreType)梭冠、保存路徑辕狰、是否支持版本遷移等)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 用于支持版本遷移的參數(shù)
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
NSError *error = nil;
NSPersistentStoreCoordinator *psc = _context.persistentStoreCoordinator;
// 備注,如果options參數(shù)傳nil控漠,表示不支持版本遷移
NSPersistentStore *store = [psc addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeURL
options:options
error:&error];
NSAssert(store != nil, @"Error initializing PSC: %@\n%@", [error localizedDescription], [error userInfo]);
});
}
(3)創(chuàng)建項目直接勾選 Use Core Data
項目模版會在 AppDelegate 類中直接幫你初始化好 Core Data Stack蔓倍。
2.創(chuàng)建 managed object model
選擇 Core Data 欄目下的 Data Model ,就可以創(chuàng)建一個 .xcdatamodeld
模型文件盐捷。
自己創(chuàng)建的默認(rèn)系統(tǒng)提供的命名為 Model.xcdatamodeld
偶翅。
添加實體、實體的特性毙驯、關(guān)系:
(1)Entity
當(dāng)在 Xcode 中點擊 Model.xcdatamodeld
時倒堕,會看到編輯視圖 Add Entity
。
如果把數(shù)據(jù)模型文件比作數(shù)據(jù)庫中的“庫”爆价,那么 Entity 就相當(dāng)于庫里的實體。
假設(shè)我這個數(shù)據(jù)模型是用來存放圖書館信息的媳搪,那么很自然的铭段,我會想建立一個叫 Book 的 Entity。
(2) Attributes
當(dāng)建立一個名為 Book 的 Entity 時秦爆,會看到視圖中有欄寫著 Attributes序愚,我們知道,當(dāng)我們定義一本書時等限,自然要定義書名爸吮,書的編碼等信息。這部分信息叫 Attributes望门,即書的屬性形娇。
(3) Relationship
在我們使用 Entity 編輯時,除了看到了Attributes 一欄筹误,還看到下面有 Relationships 一欄
在 Reader 的 Relationship 下點擊 + 號鍵桐早。然后在Relationship 欄的名字上填 borrow,表示讀者和書的關(guān)系是“借閱”,在 Destination 欄選擇 Book哄酝,這樣友存,讀者和書籍的關(guān)系就確立了。
對于第三欄陶衅,Inverse屡立,需要創(chuàng)建 Book 的 Relationship 后在關(guān)聯(lián)。
選擇 Book 的一欄搀军,在 Relationship 下添加新的 borrowBy侠驯,Destination 是 Reader,這時候點擊 Inverse 一欄奕巍,會發(fā)現(xiàn)彈出了 borrow吟策,直接點上。
那么在 Reader 的 Relationship 中的止,我們會發(fā)現(xiàn) Inverse 一欄會自動補齊為 borrowBy檩坚。因為電腦這時候已經(jīng)完全理解了雙方的關(guān)系,自動做了補齊诅福。
一對一和一對多 - to one和to many
我們建立 Reader 和 Book 之間的聯(lián)系的時候匾委,發(fā)現(xiàn)他們的聯(lián)系邏輯之間還漏了一個環(huán)節(jié)。
假設(shè)一本書被一個讀者借走了氓润,它就不能被另一個讀者借走赂乐,而當(dāng)一個讀者借書時,卻可以借很多本書咖气。
也就是說挨措,一本書只能對應(yīng)一個讀者,而一個讀者卻可以對應(yīng)多本書崩溪。
這就是 一對一 → to one 和 一對多 → to many 浅役。
3.創(chuàng)建 NSManagedObject
子類
(1)方法1
直接 Command + N 創(chuàng)建一個新類,繼承 NSManagedObject
類伶唯,然后定義的屬性和模型文件中的一致觉既。
(2)方法2
選中對應(yīng)的實體,然后 Editor > Create NSManagedObject Subclass...乳幸,系統(tǒng)自動生成 NSManagedObject
子類瞪讼。
這種方法,如果有一對多的關(guān)系粹断,會生成 2 個Category(Core Data生成的 NSManagedObject
子類符欠,都是以 Category 形式存在的)
CoreDataProperties:生成實體中 Attributes 對應(yīng)的屬性。Relationships 也會生成對應(yīng)的屬性:一對多關(guān)系是 NSSet/NSOrderSet
類型屬性(本質(zhì)是個集合)姿染,一對一關(guān)系則是非集合的對象類型屬性背亥。
CoreDataGeneratedAccessors 其實就是一系列增加秒际、刪除 NSOrderSet/NSSet
里元素的方法。(如果沒有對多關(guān)系狡汉,不會有這個 Category)