Core Data 版本遷移攻略(上)

這一部分包含了遷移的簡介,回答了遷移是什么,什么時(shí)候要遷移的問題窥突,寫了輕量級(jí)遷移 和 基于 MappingModel 的手動(dòng)遷移。

純手動(dòng)遷移 和 漸進(jìn)式遷移 放在后面硫嘶。


0. 前言概述

發(fā)布應(yīng)用新版本的時(shí)候經(jīng)常會(huì)遇到這樣頭痛的問題阻问,新版本的 Core Data 的數(shù)據(jù)模型要增加新的屬性,當(dāng)然你要對(duì) Core Data 的 Data Model 做出修改沦疾,Core Data 的遷移就是為了解決這樣的問題称近。

關(guān)于遷移渠道呢第队?Migration 會(huì)使用 映射 或 匹配,從而使得舊的 Data Model 升級(jí)為新的 Data Model刨秆。

簡單來說凳谦,任何時(shí)候你的應(yīng)用程序都會(huì)有新的變化,然而并不是每一次變更都不會(huì)更新 Data Model衡未,當(dāng)它改變時(shí)尸执,你需要提供新的 Data Model,并且提供遷移途徑缓醋。

1. Core Data 遷移概述

Core Data 進(jìn)行遷移如失,首先要把握時(shí)機(jī)。

那么在什么時(shí)機(jī)發(fā)生遷移呢送粱?在 Core Data 在程序中初始化的時(shí)候褪贵,會(huì)創(chuàng)建我那篇文章的文章中提到的Core Data Stack悉尾,反正不管怎么樣初始化咆爽,它初始化一定會(huì)把 Sotre 添加進(jìn)存儲(chǔ)化存儲(chǔ)助手也就是NSPersistentStoreCoordinator,然而遷移就是在添加這個(gè)之前發(fā)生的稚叹。

然后 Core Data 已經(jīng)把握好了這個(gè)時(shí)機(jī)动雹,他就要考慮現(xiàn)在我要不要遷移呢槽卫?這個(gè)問題。只有一種情況下會(huì)遷移洽胶,Store 的 Model 版本跟當(dāng)前的存儲(chǔ)助手配置的 Model 版本不相符的時(shí)候晒夹。當(dāng)然話說回來不相符之后你還得啟用遷移開關(guān)才行,開關(guān)沒開的話就報(bào)錯(cuò)姊氓。

那么如果開始執(zhí)行遷移:首先去分析原先的 Data Model 和你現(xiàn)在的目標(biāo) Data Model丐怯,使用這兩個(gè) Model 去創(chuàng)建 遷移映射模型(Migration mapping model),這個(gè)映射模型用來把舊的Model里面的數(shù)據(jù)轉(zhuǎn)移到新的Model里面翔横。

遷移的三個(gè)步驟:

  1. Core Data 把舊的 Store 里面的全部數(shù)據(jù)拷貝到新的一個(gè) Store 中读跷。
  2. Core Data 根據(jù)關(guān)系映射把所有的數(shù)據(jù) Object 關(guān)聯(lián)起來。
  3. 會(huì)在目標(biāo) Model 中加上數(shù)據(jù)驗(yàn)證禾唁。所以在拷貝過程中 Core Data 禁用了數(shù)據(jù)驗(yàn)證效览。

另外,只有在遷移沒有任何報(bào)錯(cuò)的情況下荡短,遷移執(zhí)行完成丐枉,舊的 Store 才會(huì)被刪除。

1.1 遷移類型

  1. 輕量級(jí)遷移: 最簡單的遷移掘托,用戶只需要設(shè)計(jì)幾個(gè)標(biāo)記瘦锹,遷移就會(huì)自動(dòng)完成。比如我們僅僅是添加實(shí)體模型和可選屬性的時(shí)候,這個(gè)時(shí)候數(shù)據(jù)層的變動(dòng)簡單到根本不需要你去告訴 Core Data 如何遷移弯院,也就是它會(huì)自動(dòng)遷移辱士,NSPersistentStoreCoordinator 就會(huì)自動(dòng)生成 Mapping Model。如果有更復(fù)雜的變化听绳,就需要自己自定義 Mapping Model 了颂碘。下文中羅列了適合輕量級(jí)遷移的各種情況。

  2. 手動(dòng)遷移: 手動(dòng)遷移需要自己實(shí)現(xiàn) Mapping 部分的工作椅挣,有 GUI 的工具可以完成头岔,可以自定義映射規(guī)則,你可以做的事情更多鼠证。比如如果你想從一個(gè) Model 中將特殊的屬性單獨(dú)拉出來創(chuàng)建一個(gè) Model切油,這個(gè)時(shí)候手動(dòng)遷移就是首選。

  3. 自定義遷移: 如果你需要快速地在你的數(shù)據(jù)模型上進(jìn)行相對(duì)復(fù)雜的改變名惩,那么自定義遷移就是為你準(zhǔn)備的。需要代碼實(shí)現(xiàn)一些對(duì)數(shù)據(jù)的遷移邏輯孕荠。這里自定義的實(shí)體遷移邏輯需要自定義一個(gè) NSEntityMigrationPolicy 子類來實(shí)現(xiàn)遷移邏輯娩鹉。

  4. 純手動(dòng)遷移: 當(dāng)自定義的遷移還是不足以滿足需求的時(shí)候,需要用戶自定義 版本檢測的邏輯 和 遷移邏輯稚伍。

2. 輕量遷移

接下來的步驟走一遍簡單的輕量遷移:

創(chuàng)建一個(gè)新的數(shù)據(jù)版本:在開始做改動(dòng)之前弯予,就是你還沒有修改你的第一版本的時(shí)候,選中 xcdatamodeld 文件然后選擇 Editor个曙,選擇 Add Model Version锈嫩,給新版本起一個(gè)名字,最簡單的--在原名字后加個(gè) v2垦搬。

可以在文件查看窗口(File Inspector pane)對(duì) xcdatamodeld 文件的 Version 進(jìn)行查看和更改『舸纾現(xiàn)在切換到剛剛創(chuàng)建的新的版本。

在新的版本中選擇一個(gè) Entity猴贰,給這個(gè)實(shí)體上添加一個(gè)新的 Property对雪,名字按需求來,類型選擇 Transformable米绕。

點(diǎn)擊新的屬性瑟捣,在右邊的編輯窗口的第三個(gè)數(shù)據(jù)模型查看器(Data Model Inspector),在上面的 Attribute Type 下面有一個(gè)Name栅干,輸入:YourProjectName.ImageTransformer(按照圖片類型舉例)迈套。

然后去這個(gè)實(shí)體的 .swift 中添加屬性:

@NSManaged var image: UIImage!

這個(gè)時(shí)候如果運(yùn)行了程序,一定會(huì)報(bào)錯(cuò)吧碱鳞。因?yàn)榍把圆糠终f了桑李,如果 CoreData 發(fā)現(xiàn)你實(shí)際存在的 Model 和你程序里面定義的Model不一致的話,它就會(huì)表示這兩個(gè)不匹配,然后拋錯(cuò)芙扎。所以先激活輕量遷移星岗。

激活輕量遷移

激活的時(shí)候你需要在初始化的時(shí)候加兩個(gè)標(biāo)記,在 Core Data Stack 中加入一個(gè)屬性:

var options: NSDictionary?

修改初始化 init 方法:

init(modelName:String, storeName:String, options: NSDictionary? = nil) {
  self.modelName = modelName
  self.storeName = storeName
  self.options = options
}

雖然修改了方法的簽名戒洼,但是option設(shè)置為nil俏橘,意思就是舊的方法簽名仍舊可用,不會(huì)造成程序其他地方需要修改圈浇。

store = coordinator.addPersistentStoreWithType( 
  NSSQLiteStoreType, 
  configuration: nil, 
  URL: storeURL, 
  options: self.options)

在創(chuàng)建 Core Data Stack 的類中寥掐,修改初始化的代碼:

lazy var stack : CoreDataStack = CoreDataStack(
  modelName:"YourModelName",
  storeName:"YourStoreName",
  options: [NSMigratePersistentStoresAutomaticallyOption: true,
          NSInferMappingModelAutomaticallyOption: true])

開關(guān)就在這了

這個(gè) NSMigratePersistentStoresAutomaticallyOption 告訴 CoreData 的 NSPersistentStoreCoordinator 如果存儲(chǔ)層的 Model 和實(shí)際的 Model 不匹配的話就開始執(zhí)行遷移。

后面的 NSInferMappingModelAutomaticallyOption 字面意思翻譯是:Model的自動(dòng)推測映射選項(xiàng)磷蜀,也就是問你要不要打開 輕量遷移召耘。MappingModel 就是遷移的映射助手,它指導(dǎo)著每個(gè)數(shù)據(jù)如何向新的Model中遷移褐隆。

其實(shí)輕量級(jí)遷移的本質(zhì)就是自動(dòng)推測映射模型污它,所以上面這里Optional就是打開輕量級(jí)遷移的選項(xiàng)。任何遷移都需要 Mapping Model庶弃,只是輕量級(jí)遷移會(huì)由系統(tǒng)根據(jù)推測來自動(dòng)幫你創(chuàng)建一個(gè) Mapping Model衫贬。

下面列舉 輕量遷移 能夠處理的各種情況:

  1. 刪除實(shí)體、屬性 或者 關(guān)系歇攻。
  2. 使用 renamingIdentifier 重新命名實(shí)體固惯、屬性 或者關(guān)系。
  3. 新添加一個(gè) Optional 的屬性缴守。
  4. 新添加一個(gè) Required 屬性葬毫,但是必須有默認(rèn)值。
  5. 把一個(gè) Optional 屬性改成帶有默認(rèn)值的 Required 屬性屡穗。
  6. 把一個(gè) 非Option 的屬性改成 Optional屬性贴捡。
  7. 改變實(shí)體結(jié)構(gòu)。
  8. 新添加父實(shí)體村砂,把屬性向父類移動(dòng)或者將父類屬性往子類中移栈暇。
  9. 把 對(duì)一 關(guān)系改成 對(duì)多 關(guān)系。
  10. 改變關(guān)系箍镜,從 non-ordered to-many 到 ordered to-many源祈。

3. 手動(dòng)遷移

當(dāng)遷移足夠簡單的時(shí)候,符合上面十種規(guī)范的時(shí)候色迂,可以使用輕量遷移全自動(dòng)完成香缺。但是來了,但是如果你想要更加靈活的遷移歇僧,手動(dòng)遷移第一種是使用 Mapping Model 去實(shí)現(xiàn)遷移图张,功能更強(qiáng)大锋拖,更加靈活。

創(chuàng)建一個(gè)新的 Data Version:

選中 xcdatamodeld 文件然后選擇 Editor祸轮,選擇 Add Model Version兽埃,給新版本起一個(gè)名字,可以在原名字后加 v3适袜。

然后在新的 v3 版本中做 Model 的修改柄错,確認(rèn)完成所有修改之后,創(chuàng)建 New File苦酱,選擇 iOSCoreData Mapping Model售貌,然后選擇 Mapping Model Source Data Model,這個(gè)選 你創(chuàng)建新的 Data Version 的之前的那本版本疫萤,然后下一步選擇Target Model Target Data Model颂跨,這個(gè)選擇剛剛的那個(gè) v3 版本。

創(chuàng)建的這個(gè)文件的名字按照約定成俗扯饶,前面是Model的名字恒削,加上MappingModel,后面加上舊的Model版本和新的Model版本尾序,ModelNameMappingModel_v2_to_v3蔓同。

屬性映射

當(dāng)映射模型創(chuàng)建出來的時(shí)候,Xcode 已經(jīng)對(duì)新舊版本進(jìn)行了推測蹲诀,已經(jīng)進(jìn)行了一些屬性的映射,所以可以在這個(gè)Xcode推測的版本上進(jìn)行屬性的映射弃揽。

新創(chuàng)建的屬性映射操作界面包含了兩部分脯爪,上面是屬性映射Attributes Mappings,下面是關(guān)系映射Relationship Mappings矿微。

在 Attributes 中配置屬性的映射痕慢,其中 Destination Attribute 目標(biāo)屬性是指新的 Model 中的 Attribute,Value Expression 指的是這項(xiàng)數(shù)據(jù)從哪里來涌矢,在其中可以使用 $source 來作為數(shù)據(jù)源實(shí)例的引用掖举。

在左邊 Enitty Mapping 中顯示的是實(shí)體映射的名稱,選擇一個(gè)實(shí)體映射可以在右邊編輯窗口修改它的名稱娜庇,命名規(guī)范為:源實(shí)體名To新實(shí)體名塔次,比如 NoteToNote,表示從遷移源中的 Note 到新的 Model 中的 Note 的遷移實(shí)體映射名秀。

右邊窗口可以在 Source 中選擇遷移源的 Model励负,選擇后 Xcode 回自動(dòng)根據(jù)屬性名來進(jìn)行一些匹配。

還可以選中左邊的 Enitty Mapping匕得,然后在右邊的 Filter Predicate 中編輯 predicate 條件继榆,比如 name != nil 這一類。

關(guān)系映射

關(guān)系映射我理解是會(huì)比屬性映射比較難理解一些。這里是第一次出現(xiàn)略吨。

你知道集币,跨實(shí)體的遷移時(shí)有會(huì)發(fā)生。舉個(gè)栗子翠忠,比如我們上一個(gè)項(xiàng)目中鞠苟,主實(shí)體是會(huì)議,會(huì)議有一個(gè)賬單數(shù)組屬性负间,后來要單獨(dú)拉出來做成一個(gè)賬單的 Model偶妖,這個(gè)時(shí)候遷移的時(shí)候要看用戶有多少賬單,我就要遷移中創(chuàng)建多少個(gè)賬單 Model政溃,并且會(huì)議和賬單的關(guān)系是一對(duì)多趾访,全部關(guān)聯(lián)好,這個(gè)實(shí)體之間關(guān)系的遷移就在這里董虱。

在 xcmappingmodel 的下半編輯區(qū)域扼鞋,是屬于 Relationship Mapping 的編輯區(qū)域,可以在這里創(chuàng)建關(guān)系映射愤诱。

右邊的屬性編輯區(qū)域中云头,可以選擇屬性,選擇 Source Fetch 的方式(Auto Generate Value Expression 是自動(dòng)生成)淫半,填寫 keyPath溃槐,還有 MappingName。

FUNCTION(
$manager,
"destinationInstancesForEntityMappingNamed:
        sourceInstances:" , 
"NoteToNote", $source)

編輯好的映射的 Value Expression 就像上面這個(gè)一樣科吭。

深入一下這個(gè) $manager昏滴,它指向一個(gè) NSMigrationManager 對(duì)象,這個(gè)對(duì)象在遷移過程中發(fā)揮作用对人,它處理著遷移的過程谣殊,Migration manager 遷移管理員 管理著源對(duì)象和目標(biāo)對(duì)象的關(guān)聯(lián)。之前有 $source牺弄,這里又有$manager姻几,Core Data還提供了別的一些對(duì)象可以在 xcmappingmodel 中使用

NSMigrationManagerKey: $manager

NSMigrationSourceObjectKey: $source

NSMigrationDestinationObjectKey: $destination

NSMigrationEntityMappingKey: $entityMapping

NSMigrationPropertyMappingKey: $propertyMapping

NSMigrationEntityPolicyKey: $entityPolicy

NSEntityMigrationPolicy 自定義遷移的寫在后面。

到這里梳理一下:基于 Mapping Model 的手動(dòng)遷移需要做什么势告?

  1. 創(chuàng)建新的 Data Version 并作好新數(shù)據(jù)版本的修改工作
  2. 創(chuàng)建 xcdatamodeld 并配置好 屬性映射 和 關(guān)系映射
  3. 配置初始化

第三步就是要改一行代碼蛇捌,修改 Core Data Stack:

options: [NSMigratePersistentStoresAutomaticallyOption: true,
          NSInferMappingModelAutomaticallyOption: false]

給 NSInferMappingModelAutomaticallyOption 設(shè)置為 False,持久化存儲(chǔ)助手就會(huì)使用 MappingModel 去遷移存儲(chǔ)層咱台。

深入:那這里發(fā)生了什么豁陆?Core Data 發(fā)現(xiàn)不使用輕量級(jí)自動(dòng)遷移,因?yàn)槭?false 嘛吵护,然后它就會(huì)在 bundle 里面去查找它需要的 mapping model盒音,因?yàn)樗喇?dāng)前的Model是從哪一版遷移到哪一版的表鳍,所以它很明確bundle 中的哪一個(gè)文件是它當(dāng)前需要的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末祥诽,一起剝皮案震驚了整個(gè)濱河市譬圣,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌雄坪,老刑警劉巖厘熟,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異维哈,居然都是意外死亡绳姨,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門阔挠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來飘庄,“玉大人,你說我怎么就攤上這事购撼」蛳鳎” “怎么了?”我有些...
    開封第一講書人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵迂求,是天一觀的道長碾盐。 經(jīng)常有香客問我,道長揩局,這世上最難降的妖魔是什么毫玖? 我笑而不...
    開封第一講書人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮凌盯,結(jié)果婚禮上付枫,老公的妹妹穿的比我還像新娘。我一直安慰自己十气,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開白布春霍。 她就那樣靜靜地躺著砸西,像睡著了一般。 火紅的嫁衣襯著肌膚如雪址儒。 梳的紋絲不亂的頭發(fā)上芹枷,一...
    開封第一講書人閱讀 51,274評(píng)論 1 300
  • 那天,我揣著相機(jī)與錄音莲趣,去河邊找鬼鸳慈。 笑死,一個(gè)胖子當(dāng)著我的面吹牛喧伞,可吹牛的內(nèi)容都是我干的走芋。 我是一名探鬼主播绩郎,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼翁逞!你這毒婦竟也來了肋杖?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤挖函,失蹤者是張志新(化名)和其女友劉穎状植,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體怨喘,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡津畸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了必怜。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肉拓。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖棚赔,靈堂內(nèi)的尸體忽然破棺而出帝簇,到底是詐尸還是另有隱情,我是刑警寧澤靠益,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布丧肴,位于F島的核電站,受9級(jí)特大地震影響胧后,放射性物質(zhì)發(fā)生泄漏芋浮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一壳快、第九天 我趴在偏房一處隱蔽的房頂上張望纸巷。 院中可真熱鬧,春花似錦眶痰、人聲如沸瘤旨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽存哲。三九已至,卻和暖如春七婴,著一層夾襖步出監(jiān)牢的瞬間祟偷,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來泰國打工打厘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留修肠,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓户盯,卻偏偏與公主長得像嵌施,于是被迫代替她去往敵國和親饲化。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354

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