Core Data 迁移与版本管理
大家在學習和使用Core Data過程中,第一次進行版本遷移的經歷一定是記憶猶新,至少我是這樣的,XD。弄的不好,就會搞出一些由于遷移過程中數據模型出錯導致的Crash。這里總結了一下Core Data版本遷移過程中的經驗,希望對大家有用。
寫在前面
關于Core Data版本遷移,這兩篇文章都進行了分析,大家可以參考。
- Core Data Model Versioning and Data Migration Programming Guide
- 自定義 Core Data 遷移
遷移準備
1) 選中工程中的?xcdaramodeId?文件,Menu->Editor->Add Model Version?
這一步添加完成之后,工程中的*xcdaramodeId* 文件將會被展開,并且出現了新增加的Model文件
2) 在Xcode右側的輔助工具欄中找到 Model Version, 選擇剛剛添加的Model文件,這個時候你會發現Xcode目錄中,Model文件上的綠色的勾選中了當前選擇的Model文件
3) 在新的Model文件中修改最新的Entities等信息,記得也同時修改NSManagedObject Subclass對應的實現
4) 修改?NSPersistentStoreCoordinator?部分實現:?
let modelFilename = "the model file name in your project" let modelPath = NSBundle.mainBundle().pathForResource(modelFIlename, ofType: "momd")let managedObjectModel = NSManagedObjectModel(contentsOfURL: NSURL.fileURLWithPath(modelPath) let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel) // 這里是添加的部分,名如其意,當我們需要自動版本遷移時,我們需要在addPersistentStoreWithType方法中設置如下options let options = [NSInferMappingModelAutomaticallyOption: true, NSMigratePersistentStoresAutomaticallyOption: true] var error: NSError? = nil persistentStoreCoordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: options, error: &error)輕量級遷移
當我們僅僅是對數據模型增加實體或者可選屬性時,上述步驟完成后運行代碼進行遷移是奏效的。這個過程文檔中叫做?Lightweight Migration?,當我們進行輕量級遷移時,?NSPersistentStoreCoordinator?會為我們自動推斷出一個?Mapping Model?。如果有更加復雜的改變,我們就需要自己去實現Mapping Mode。?
添加Mapping Model過程: New File->CoreData->Mapping Model, 選擇我們需要進行Mapping的兩個Model,最終會生成一個 *xcmappingmodel* 文件,大家可以打開文件,看到里面生成了Model之間的映射。
官方文檔中介紹如下的改變支持輕量級遷移:
- 為Entity簡單的添加一個屬性
- 為Entity移除一個屬性
- 屬性值由 Optional<-> Non-optional 之間轉換
- 為屬性設置 Default Value
- 重命名Entity或者Attribute
- 增加一個新的relationship 或者刪除一個已經存在的 relationship
- 重命名relationship
- 改變relationship to-one<-> to-many 等
- 增加,刪除Entities
- 增加新的 Parent 或者 Child Entity
- 從Hierarchy中移除Entities
輕量級遷移不支持合并Entity的層級:比如在舊的Model中兩個已知的Entities沒有共享一個共同的Parent Entity,那么在新的Model中它們也不能夠共享一個共同的Parent Entity。
在為屬性或者Entity等重命名時,我們需要在Xcode右側輔助工具欄中找到 Versioning -> RenamingID,設置Reanaming Identifier為之前對應的名稱。
Mapping Models
如果我們對數據模型的修改不支持輕量級遷移,我們就需要像上文中所說的那樣,自己創建Mapping Model。
打開創建好的?xcmappingmodel?文件,我們發現可以增加或者修改對應的 Entity Mappings, Attibute Mappings 和 Relationship Mappings。?
Core Data提供了如下一組變量允許我們進行配置:
NSMigrationManagerKey: $managerNSMigrationSourceObjectKey: $source NSMigrationDestinationObjectKey: $destination NSMigrationEntityMappingKey: $entityMapping NSMigrationPropertyMappingKey: $propertyMapping NSMigrationEntityPolicyKey: $entityPolicy有時候,我們不僅僅需要修改Entity的屬性或者關系,可以使用?NSEntityMigrationPolicy?自定義整個遷移的過程。繼承?NSEntityMigrationPolicy?實現遷移過程,然后選中對應的Entity Mapping,在Xcode右側輔助工具欄中找到Custom Policy,并設置為實現遷移對應的類名。?
NSEntityMigrationPolicy
NSEntityMigrationPolicy目前提供了7個方法可供實現,它們的調用順序如下:
1) 當遷移將要開始時,會調用
func beginEntityMapping(mapping: NSEntityMapping, manager: NSMigrationManager, error: NSErrorPointer) -> Bool2) 在舊數據上構建新的實例時調用
func createDestinationInstancesForSourceInstance(sInstance: NSManagedObject, entityMapping mapping: NSEntityMapping, manager: NSMigrationManager, error: NSErrorPointer) -> Bool結束時調用
func endInstanceCreationForEntityMapping(mapping: NSEntityMapping, manager: NSMigrationManager, error: NSErrorPointer) -> Bool3) 構建新的RelationShips調用
func createRelationshipsForDestinationInstance(dInstance: NSManagedObject, entityMapping mapping: NSEntityMapping, manager: NSMigrationManager, error: NSErrorPointer) -> Bool`結束時調用
func endRelationshipCreationForEntityMapping(mapping: NSEntityMapping, manager: NSMigrationManager, error: NSErrorPointer) -> Bool4) 驗證,保存數據調用
func performCustomValidationForEntityMapping(mapping: NSEntityMapping, manager: NSMigrationManager, error: NSErrorPointer) -> Bool5) 遷移結束時調用
func endEntityMapping(mapping: NSEntityMapping, manager: NSMigrationManager, error: NSErrorPointer) -> Bool遷移過程
這里分享的是自己項目中數據自定義遷移的整個過程,并附上部分代碼實現邏輯。項目中采用的是漸進式遷移,漸進式遷移的概念在?自定義 Core Data 遷移?一文中有介紹:?
// 這段文字取自 <<自定義 Core Data 遷移>> 一文想像一下你剛剛部署一個包含版本 3 的數據模型的更新。你的某個用戶已經有一段時間沒有更新你的應用了,這個用戶還在版本 1 的數據模型上。那么現在你就需要一個從版本 1 到版本 3 的映射模型。同時你也需要版本 2 到版本 3 的映射模型。當你添加了版本 4 的數據模型后,那你就需要創建三個新的映射模型。顯然這樣做的擴展性很差,那就來試試漸進式遷移吧。與其為每個之前的數據模型到最新的模型間都建立映射模型,還不如在每兩個連續的數據模型之間創建映射模型。以前面的例子來說,版本 1 和版本 2 之間需要一個映射模型,版本 2 和版本 3 之間需要一個映射模型。這樣就可以從版本 1 遷移到版本 2 再遷移到版本 3。顯然,使用這種遷移的方式時,若用戶在較老的版本上遷移過程就會比較慢,但它能節省開發時間并保證健壯性,因為你只需要確保從之前一個模型到新模型的遷移工作正常即可,而更前面的映射模型都已經經過了測試。1) 判斷本地SQLite數據庫文件是否存在,不存在直接退出整個遷移。
2) 檢測當前本地數據庫和數據模型是否一致,如果一致就退出遷移。
let storeURL = NSURL(fileURLWithPath: "SQLite file path") let managedObjectModel: NSManagedObjectModel = Your current managed object modellet sourceMetadata = NSPersistentStoreCoordinator.metadataForPersistentStoreOfType(NSSQLiteStoreType, URL:storeURL!, error: nil) Bool needMigration = managedObjectModel.isConfiguration(nil, compatibleWithStoreMetadata: sourceMetadata)3) 取得當前數據庫存儲時用的數據模型,如果獲取不到或者獲取失敗,退出遷移。
if let sourceModel = NSManagedObjectModel.mergedModelFromBundles(nil, forStoreMetadata: sourceMetadata!) {println("\(sourceModel)") } else {return }4) 取得當前工程中所有數據模型對應的managedObjectModel用于遷移,如果獲取的結果少于兩個就退出遷移。
5) 從所有的managedObjectModel中遍歷出最終使用的Model和當前數據庫采用的Model之間的所有Model,按照version順序構建一個新的 Model list,確保第一個是sourceModel,最后一個是當前需要使用的managedObjectModel。
6) 對生成的這個Model list進行循環,開始漸進式遷移。
7) 構建Model list中相鄰兩個Model之間的NSMappingModel實例,用做遷移。
for var index = 0; index < modelList.count - 1; index++ { let modelA = modelList[index] let modelB = modelList[index + 1] //檢查是否有自定義的Mapping model存在 var mappingModel : NSMappingModel? = NSMappingModel(fromBundles: nil, forSourceModel: modelA, destinationModel: modelB) //如果不存在,嘗試infer一個 mappingModel = NSMappingModel.inferredMappingModelForSourceModel(modelA, destinationModel: modelB, error: nil) //如果最終取不到Mapping Model 就退出遷移 //如果得到了Mapping Model,就可以開始進行遷移 }8) 終于可以開始進行遷移了,XD
9) 創建一個新的文件路徑用來存儲遷移過程中的數據文件
10) 使用上文中的 modelA 和 modelB 構建一個?NSMigrationManager?實例,使用?
func migrateStoreFromURL(sourceURL: NSURL, type sStoreType: String, options sOptions: [NSObject : AnyObject]?, withMappingModel mappings: NSMappingModel?, toDestinationURL dURL: NSURL, destinationType dStoreType: String, destinationOptions dOptions: [NSObject : AnyObject]?, error: NSErrorPointer) -> Bool方法進行遷移,其中?toDestinationURL?參數是我們在步驟9中創建的路徑。如果migrate失敗,就退出整個遷移過程。?
11) 數據替換 1.把原始的source文件移動到新的backup文件夾中 2.使用步驟9中文件下生成的遷移之后的數據文件移動到原始的數據的路徑下 3.刪除backup
12) 回到步驟7,進行下一次迭代遷移,直到結束
遷移調試
我們可以在Xcode中設置啟動參數?-com.apple.coredata.ubiquity.logLevel 3?或者?-com.apple.CoreData.SQLDebug 1?, 這樣在程序運行時,控制臺將會打印更多Core Data使用中的信息,包括調用的SQL語句。?
轉載于:https://www.cnblogs.com/lihaibo-Leao/p/5536930.html
總結
以上是生活随笔為你收集整理的Core Data 迁移与版本管理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: TOPCODER SAM 686 div
- 下一篇: 有关接口 笔记 懒人版