Core Data 版本迁移经验总结

大家在学习和使用Core Data过程中,第一次进行版本迁移的经历一定是记忆犹新,至少我是这样的,XD。弄的不好,就会搞出一些由于迁移过程中数据模型出错导致的Crash。这里总结了一下Core Data版本迁移过程中的经验,希望对大家有用。

写在前面

关于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 -&gt; RenamingID,设置Reanaming Identifier为之前对应的名称。

Mapping Models

如果我们对数据模型的修改不支持轻量级迁移,我们就需要像上文中所说的那样,自己创建Mapping Model。

打开创建好的 xcmappingmodel 文件,我们发现可以增加或者修改对应的 Entity Mappings, Attibute Mappings 和 Relationship Mappings。 

Core Data提供了如下一组变量允许我们进行配置:

NSMigrationManagerKey: $manager

NSMigrationSourceObjectKey: $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) -> Bool

2) 在旧数据上构建新的实例时调用

func createDestinationInstancesForSourceInstance(sInstance: NSManagedObject, entityMapping mapping: NSEntityMapping, manager: NSMigrationManager, error: NSErrorPointer) -> Bool

结束时调用

func endInstanceCreationForEntityMapping(mapping: NSEntityMapping, manager: NSMigrationManager, error: NSErrorPointer) -> Bool

3) 构建新的RelationShips调用

func createRelationshipsForDestinationInstance(dInstance: NSManagedObject, entityMapping mapping: NSEntityMapping, manager: NSMigrationManager, error: NSErrorPointer) -> Bool`

结束时调用

func endRelationshipCreationForEntityMapping(mapping: NSEntityMapping, manager: NSMigrationManager, error: NSErrorPointer) -> Bool

4) 验证,保存数据调用

func performCustomValidationForEntityMapping(mapping: NSEntityMapping, manager: NSMigrationManager, error: NSErrorPointer) -> Bool

5) 迁移结束时调用

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 model

let 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语句。 

时间: 2024-11-01 10:44:37

Core Data 版本迁移经验总结的相关文章

关于大数据量下Core Data的数据迁移

Core Data版本迁移基础 通常,在使用Core Data的iOS App上,不同版本上的数据模型变更引发的数据迁移都是由Core Data来负责完成的.这种数据迁移模式称为Lightweight Migration(可能对于开发人员来说是lightweight),开发人员只要在添加Persistent Store时设置好对应选项,其它的就交付给Core Data来做了: 从命名上可以看出这两个选项分别代表:自动迁移Persistent Store,以及自动创建Mapping Model.

《Core Data应用开发实践指南》一3.4 默认的迁移方式

3.4 默认的迁移方式 有时候我们需要比轻量级迁移更为精细的控制手段.比方说,我们要把Measurement实体替换成另外一个名叫Amount的实体,并且还想把Measurement实体中名叫abc的那个属性迁移到Amount实体中的xyz 属性上面.abc中已有的数据也要迁移到xyz属性.为了完成这些需求,开发者需要创建模型映射,以便手工指明映射关系.在添加持久化存储区时,即便NSInferMappingModelAutomaticallyOption选项设为YES,Core Data也还是会

《Core Data应用开发实践指南》一3.5 通过迁移管理器来迁移数据

3.5 通过迁移管理器来迁移数据 除了通过NSPersistentStoreCoordinator来迁移存储区之外,还可以采用迁移管理器来做.迁移管理器可以使开发者全权掌控迁移过程中创建的文件,从而令他们能够按自己的方式来灵活处理迁移中的各种问题.使用迁移管理器的一个好处就是可以向用户报告迁移进度,使用户知道应用程序哪次会启动得比较慢一些,所以需要耐心等待.虽说迁移过程理应执行得非常快才对,但当数据库比较大.变动比较复杂时,迁移过程就需要耗费一定的时间了.为了使用户界面保持流畅,迁移过程必须在后

《Core Data应用开发实践指南》一3.3 轻量级的迁移方式

3.3 轻量级的迁移方式 把新模型设为当前版本之后,必须迁移现有的持久化存储区,只有这样,才能正常使用新模型.这是因为,持久化存储区协调器会试着用新版的模型来打开原有的存储区,但由于原有的存储区是用旧版模型创建的,所以该操作会失败.在向NSPersis-tentStoreCoordinator添加存储区的时候,只需将下列选项放在NSDictionary里传过去,即可自动完成存储区的迁移工作:如果传给NSPersistentStoreCoordinator的NSMigratePersistentS

《Core Data应用开发实践指南》一3.2 添加模型版本

3.2 添加模型版本 为了不使应用程序像图3-1那样崩溃,我们需要在修改模型之前先创建新的模型版本.添加新模型之后,就不应该再删除旧版的模型了.旧的模型有助于把原来的持久化存储区迁移到当前的模型版本.假如用户的设备上原来就没有持久化存储区,那么可以先不考虑模型版本控制问题,等到应用程序在App Store上架之后再说.请按下列步骤修改Grocery Dude,以便添加模型版本: 选中Model.xcdatamodeld. 点击Editor>Add Model Version...菜单项. 点击F

Core Data浅谈系列之四 : 数据模型的版本变迁

继上一篇文章末尾提到的,一支队伍可以添加多名球员,不过一名球员只能属于一支队伍中,这分别对应着Core Data中一对多和一对一的属性关系: 如上两图,是在Team实体里面添加了一个players关系,指向Player实体,可以一支球队关联多名球员,并且最多只允许关联15名球员. 同样地,也为Player实体添加team关系,指向Team实体: 一名球员只能关联一支球队,并且让这个关系成双向的,即一个Player对象属于某支球队时,该球队的players属性就自动关联该Player对象. 做完以

iOS 开发中使用 Core Data 应避免的十个错误

Core Data是苹果针对Mac和iOS平台开发的一个框架主要用来储存数据.对很多开发者来说Core Data比较容易入手但很难精通如果没有正确的学习方法你将很难真正理解它更不用说精通了.很多开发者常常在这方面犯一些错误而这篇文章列出了 开发者在iOS开发过程中使用Core Data常见的一些错误并对如何避免这些错误进行了分析. 1.不了解关键术语 对于iOS开发者来说会使用Core Data是一项必备技能. 没有它很多app都不会存在.当在互联网上四处搜索Core Data学习教程你很容易被

《Core Data应用开发实践指南》一3.1 修改托管对象模型

3.1 修改托管对象模型 在应用程序的进化过程中,其托管对象模型也可能需要改变.对于一些比较简单的修改,诸如设定属性的默认值.设定验证规则.使用获取请求模板等,是可以直接实施的.而对于另外一些更为结构化的(structural)修改,则需先把持久化存储区迁移到新的模型版本才行.假如没有提供迁移数据所需的映射与设定,那么应用程序就会崩溃.为了继续构建范例程序,需要把上一章中的代码添加到Grocery Dude项目中.或者可以去http://www.timroadley.com/LearningCo

在 Swift Playgrounds 中使用 Core Data 模型

本文讲的是在 Swift Playgrounds 中使用 Core Data 模型, 你能在 Xcode 的 Swift Playgrounds 中使用 Core Data 模型么?当然可以! 在2015年, http://www.learncoredata.com的作者 Jeremiah Jessel,写了篇文章 detailing how you can use the Core Data framework inside a playground.从建立 Core Data 堆栈到在代码中