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

一栅贴、簡介

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)用 NSPersistentContainerloadPersistentStoresWithCompletionHandler: 完成初始化
(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)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末娄徊,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子盾戴,更是在濱河造成了極大的恐慌寄锐,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件尖啡,死亡現(xiàn)場離奇詭異橄仆,居然都是意外死亡,警方通過查閱死者的電腦和手機衅斩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進店門盆顾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人畏梆,你說我怎么就攤上這事您宪。” “怎么了奠涌?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵宪巨,是天一觀的道長。 經(jīng)常有香客問我溜畅,道長捏卓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任慈格,我火速辦了婚禮怠晴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘峦椰。我一直安慰自己龄寞,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布汤功。 她就那樣靜靜地躺著,像睡著了一般溜哮。 火紅的嫁衣襯著肌膚如雪滔金。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天茂嗓,我揣著相機與錄音餐茵,去河邊找鬼。 笑死述吸,一個胖子當(dāng)著我的面吹牛忿族,可吹牛的內(nèi)容都是我干的锣笨。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼道批,長吁一口氣:“原來是場噩夢啊……” “哼错英!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起隆豹,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤椭岩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后璃赡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體判哥,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年碉考,在試婚紗的時候發(fā)現(xiàn)自己被綠了塌计。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡侯谁,死狀恐怖锌仅,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情良蒸,我是刑警寧澤技扼,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站嫩痰,受9級特大地震影響剿吻,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜串纺,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一丽旅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧纺棺,春花似錦榄笙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至巨朦,卻和暖如春米丘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背糊啡。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工拄查, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人棚蓄。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓堕扶,卻偏偏與公主長得像碍脏,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子稍算,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,060評論 2 355

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