iOS 數據存儲(五) -持久化 Core Data

一、簡介

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、NSPersistentStoreCoordinatorNSManagedObjectModel、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 對象
  • 調用 NSPersistentContainerloadPersistentStoresWithCompletionHandler: 完成初始化
(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)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容