一、簡介
Core Data 是蘋果官方提供的管理數據層對象的框架,它提供了對象—關系映射(ORM)的功能,即能夠將 Objective-C 對象轉化成數據,保存在 SQLite 數據庫文件中,也能夠將保存在數據庫中的數據還原成 Objective-C 對象。在此數據操作期間,不需要編寫任何 SQL 語句。Core Data 針對對象生命周期,以及持久化的對象圖管理(object graph management)中的一些常見問題提供了解決方案。CoreData 的主要任務是負責數據更改的管理、序列化到磁盤、最小化內存占用以及查詢數據。
Core Data 比 SQLite 做了更進一步的封裝,SQLite 提供了數據的存儲模型,并提供了一系列 API,你可以通過 API 讀寫數據庫,去處理想要處理的數據。但是 SQLite 存儲的數據和你編寫代碼中的數據(比如一個類的對象)并沒有內置的聯系,必須你自己編寫代碼去一一對應。
而 Core Data 卻可以解決一個數據在持久化層和代碼層的一一對應關系。也就是說,你處理一個對象的數據后,通過保存接口,它可以自動同步到持久化層里,而不需要你去實現額外的代碼。
iOS10 中利用 NSPersistentContainer
iOS10 之前涉及 NSManagedObjectContext
、NSPersistentStoreCoordinator
、NSManagedObjectModel
、NSPersistentStore
這些類。
1.Core Data Stack
說是一個對象的集合,由4個主要對象構成:
- managed object context (NSManagedObjectContext)
- persistent store coordinator (NSPersistentStoreCoordinator)
- managed object model (NSManagedObjectModel)
- persistent container (NSPersistentContainer)
Core Data Stack,就是進行數據增刪查改、保存的工作臺,Apple 提供這樣一個工作臺,讓你方便進行數據的保存。無需關心實現細節。
2.Persistent Container
NSPersistentContainer
是iOS 10、 macOS 10.12 之后才出現的新類。引入這個新類的目的之一,就是為了簡化創建 Core Data Stack 這個工作臺的過程。所以,在 iOS10 之前,創建 Core Data Stack 會復雜一些。
而 Persistent Container 也有另一個新類 NSPersistentStoreDescription
,可以利用這個類,進行一些定制化設置,比如自定義存儲路徑、設置存儲數據方式等(Core Data 支持 SQLite、XML、Binary、InMemory 4中方式存儲數據)。
備注:iOS10中,如果利用 NSPersistentContainer
創建 Core Data Stack,預設的是 NSSQLiteStoreType
類型。并且默認打開了自動輕量化版本遷移功能(換言之,在 iOS10 之前,需要手動進行相關設置,才能打開版本遷移功能)。
3.Managed Object Context。
可以理解為是一塊內存,提供了和 Managed Objects 交互的場所。也稱為:The Context 或者 MOC。NSManagedObjectContext
類實例。
備注:對數據進行刪除、保存、查詢,都要用到 NSManagedObjectContext
類的相關方法。
4.Managed Object Model
直觀點,你可以把它理解為就是 Xcode 中后綴為 xcdatamodel
的文件。在這個文件里,你可以通過非代碼、可視化的方式,定義對象、對象的屬性、對象之間的關系(Core Data 把對象稱呼為實體、對象的屬性稱呼為特性)。
Managed Object Model 就是 Core Data 中用于描述實體、實體特性、實體間關系的一套方案。
它是 NSManagenObjectModel
的類實例(也可以通過純代碼實現 .xcdatamodel
文件的內容)。
Entity - 實體
NSEntityDescription
類實例,用于定義一個對象。一個實體,最少要有名字和類名(如果沒有設置類名,默認是 NSManagedObject
類)。
Attribute - 特性
實體特性 NSAttributeDescription
類實例。就是 Entity 的特性,對應 App 中的創建類時的屬性。
Relationship - 關系
實體關系 NSRelationshipDescription
類實例。用于描述 Entity 之間的關系。
5.Managed Object。
就是需要保存的數據,是 NSManagenObject
類實例。(對應 App 中的對象)
就我的理解,Managed Object 和上面提到的 Entity,本質上是同一個東西,就是你的數據對象,只不過是在可視化操作和純代碼操作中的不同稱謂。
6.Persistent Store Coordinator
協調 Context 和 Persistent Store 的一個角色。NSPersistentStoreCoordinator
類實例。
如果只是對數據進行簡單的增刪查改,我們并不需要接觸到這個類。
7.Persistent Store
可以理解為保存數據的地方。用于設置保存數據的方式、以及保存的路徑等。(保存數據的方式指 SQLite、XML、Binary、InMemory 4種)。NSPersistentStore
類實例。也稱為 The Store 或者 Database。
在 iOS10 之前,如果需要支持版本遷移功能,需要在創建 NSPersistentStore
類實例時,傳入相應的 options
參數。而在 iOS10 中,則會自動打開版本遷移功能,并默認設置數據類型為 NSSQLiteStoreType
。
版本遷移,假如修改了數據模型(比如修改了 . xcdatamodel
文件:增加了實體,增加了特性等等?),為了防止使用者在更新 App 后,由于數據模型不一致導致崩潰,需要進行一定的處理,這個處理叫版本遷移。
二、使用
1.初始化 Core Data Stack
不過由于 iOS10 新引進了 NSPersistentContainer
類,然后新建項目又可以選擇勾選 Core Data 與否。所以情況變得稍稍有點復雜。
這里分三種情況:1、在既有項目(只需支持iOS10)初始化 Core Data Stack;2、在既有項目(需兼容iOS8、9、10等系統)初始化 Core Data Stack;3、新建項目時直接勾選了 Core Data。
(1)在既有項目添加 Core Data 功能(只需支持iOS10)
// 我們先聲明了一個NSPersistentContainer類型的屬性:persistentContainer,在適合的時間調用initWithName:對其初始化
// 這里的Name參數,需要和后續創建的.xcdatamodeld模型文件名稱一致。
_persistentContainer = [[NSPersistentContainer alloc] initWithName:@"Model"];
// 調用loadPersistentStoresWithCompletionHandler:方法,完成Core Data Stack的最中初始化。
// 如果不能初始化成功,在Block回調中打印錯誤,方便調試
[_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription * _Nonnull description, NSError * _Nullable error) {
if (error != nil) {
NSLog(@"Fail to load Core Data Stack : %@", error);
abort();
}
else {
...
}
}];
分為兩步:
- 初始化
NSPersistentContainer
對象 - 調用
NSPersistentContainer
的loadPersistentStoresWithCompletionHandler:
完成初始化
(2)在既有項目初始化 Core Data Stack(需兼容iOS8、9、10等系統)
- (instancetype)init
{
self = [super init];
if (self) {
NSInteger majorVersion = [NSProcessInfo processInfo].operatingSystemVersion.majorVersion;
if (majorVersion < 10) {
// iOS10以下的系統, 用舊有的方法初始化Core Data Stack
[self initializeCoreDataLessThaniOS10];
}
else {
// iOS10的系統, 用新的方法(詳見上面介紹的情況1)
[self initializeCoreData];
}
}
return self;
}
- (void)initializeCoreDataLessThaniOS10 {
// Get managed object model(拿到模型文件,也就是.xcdatamodeld文件(我們會在初始化完Core data Stack后創建))
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(創建NSPersistentStoreCoordinator對象(需要傳入上述創建的NSManagedObjectModel對象))
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
// Creat managed object context(創建NSManagedObjectContext對象(_context是聲明在.h文件的屬性——因為其他類也要用到這個屬性))
_context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
// assgin persistent store coordinator(賦值persistentStoreCoordinator)
_context.persistentStoreCoordinator = psc;
// Create .sqlite file(在沙盒中創建.sqlite文件)
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *documentsURL = [[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *storeURL = [documentsURL URLByAppendingPathComponent:@"DataModel.sqlite"];
// Create persistent store(異步創建NSPersistentStore并add到NSPersistentStoreCoordinator對象中,作用是設置保存的數據類型(NSSQLiteStoreType)、保存路徑、是否支持版本遷移等)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 用于支持版本遷移的參數
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
NSError *error = nil;
NSPersistentStoreCoordinator *psc = _context.persistentStoreCoordinator;
// 備注,如果options參數傳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)創建項目直接勾選 Use Core Data
項目模版會在 AppDelegate 類中直接幫你初始化好 Core Data Stack。
2.創建 managed object model
選擇 Core Data 欄目下的 Data Model ,就可以創建一個 .xcdatamodeld
模型文件。
自己創建的默認系統提供的命名為 Model.xcdatamodeld
。
添加實體、實體的特性、關系:
(1)Entity
當在 Xcode 中點擊 Model.xcdatamodeld
時,會看到編輯視圖 Add Entity
。
如果把數據模型文件比作數據庫中的“庫”,那么 Entity 就相當于庫里的實體。
假設我這個數據模型是用來存放圖書館信息的,那么很自然的,我會想建立一個叫 Book 的 Entity。
(2) Attributes
當建立一個名為 Book 的 Entity 時,會看到視圖中有欄寫著 Attributes,我們知道,當我們定義一本書時,自然要定義書名,書的編碼等信息。這部分信息叫 Attributes,即書的屬性。
(3) Relationship
在我們使用 Entity 編輯時,除了看到了Attributes 一欄,還看到下面有 Relationships 一欄
在 Reader 的 Relationship 下點擊 + 號鍵。然后在Relationship 欄的名字上填 borrow,表示讀者和書的關系是“借閱”,在 Destination 欄選擇 Book,這樣,讀者和書籍的關系就確立了。
對于第三欄,Inverse,需要創建 Book 的 Relationship 后在關聯。
選擇 Book 的一欄,在 Relationship 下添加新的 borrowBy,Destination 是 Reader,這時候點擊 Inverse 一欄,會發現彈出了 borrow,直接點上。
那么在 Reader 的 Relationship 中,我們會發現 Inverse 一欄會自動補齊為 borrowBy。因為電腦這時候已經完全理解了雙方的關系,自動做了補齊。
一對一和一對多 - to one和to many
我們建立 Reader 和 Book 之間的聯系的時候,發現他們的聯系邏輯之間還漏了一個環節。
假設一本書被一個讀者借走了,它就不能被另一個讀者借走,而當一個讀者借書時,卻可以借很多本書。
也就是說,一本書只能對應一個讀者,而一個讀者卻可以對應多本書。
這就是 一對一 → to one 和 一對多 → to many 。
3.創建 NSManagedObject
子類
(1)方法1
直接 Command + N 創建一個新類,繼承 NSManagedObject
類,然后定義的屬性和模型文件中的一致。
(2)方法2
選中對應的實體,然后 Editor > Create NSManagedObject Subclass...,系統自動生成 NSManagedObject
子類。
這種方法,如果有一對多的關系,會生成 2 個Category(Core Data生成的 NSManagedObject
子類,都是以 Category 形式存在的)
CoreDataProperties:生成實體中 Attributes 對應的屬性。Relationships 也會生成對應的屬性:一對多關系是 NSSet/NSOrderSet
類型屬性(本質是個集合),一對一關系則是非集合的對象類型屬性。
CoreDataGeneratedAccessors 其實就是一系列增加、刪除 NSOrderSet/NSSet
里元素的方法。(如果沒有對多關系,不會有這個 Category)