修改托管对象模型
在应用程序的进行过程中,其托管对象模型也可能需要进行修改。对于一些比较简单的修改,诸如设定属性的默认值、设定验证规则、使用获取请求模板等,是可以直接实施的。而对于另外一些更为结构化的修改,则需要把持久化存储区迁移到新的模型版本才行。假如没有提供迁移数据所需要的映射与设定,那么应用程序就会崩溃。
引发模型不兼容错误
在托管对象模型中添加另外一个新的实体,并且在新的实体中添加一些新的属性。
重新运行应用程序,你会发现程序会崩溃掉。
对于处在开发初期的应用程序来说,这种崩溃算不上什么大的问题,我们只需要吧程序删除了并重新运行一次就好了。删除之后,在次运行应用程序时,它会按照最新的模型来创建持久化存储区。这样一来,存储区就可以和模型相兼容了,于是应用程序也就不会再次崩溃了。但是,这样做也会失去存储区里原有的数据。对于已经在App Store上架的程序来说,这让人无法接受。有好几种办法都可以迁移现有的持久化存储区,而迁移路径则是由变更的复杂程度以及是否使用iCloud等因素来决定的。无论采用哪种迁移办法,我们都必须首先熟悉“模型版本控制”。
添加模型版本
按照以下步骤来添加模型版本:
1、选中Model.xcdatamodeld
2、点击Editor> Add Model Version...菜单项
3、点击Finish,将Model2用作版本名称。
现在项目中会有两个版本的模型了。如图:
Model2.xcdatamodel这个新的模型的内容一开始便与Model.xcdatamodel完全相同。
我们现在选中Model2.xcdatamodel这个新的模型,在这个新的模型中添加Measurement实体,创建名叫abc的属性,并将其类型设置为String。
添加了新的版本模型之后,必须将其设置为当前版本,然后才能让应用程序使用它。
选中Model.xcdatamodeld在右侧将 Current Model Version设置为Model2.如图:
如果想正常运行应用程序,那么我们还需要配置好迁移选项,告诉Core Data应该如何去迁移。要是现在就去运行应用程序的话,那自然还是会发生Store is incompatible(存储区不兼容)错误。
轻量级的迁移方式
把新的模型设置为当前版本之后,必须迁移现有的持久化存储区,只有这样,才能正常使用新模型。这是因为,持久化存储区协调器会试着使用新的模型来打开原有的存储区,但是原有的存储区是用旧版本的模型创建的,所以会出现错误。在向NSPersistentStoreCoordinator添加存储区的时候,只需要将下列选项放在NSDictionary里面传过去,即可自动完成存储区的迁移工作:
如果传给NSPersistentStoreCoordinator的NSMigratePersistenStoresAutomaticallyOption是YES,那么Core Data 就会试着把低版本的持久化存储区迁移到最新版本的模型。
如果传给NSPersistentStoreCoordinator的NSInferMappingModelAutomaticallyOption是YES,那么Core Data 就会试着以最为合理地方式自动推断出源模型实体中的某个属性到底对应于“目标模型实体”中的哪一个属性。
打开CoreDataHelper.swift同时添加一个属性在这个类中。
lazy var options: NSDictionary?
Right now, you’re setting up the persistent store with no options for default behavior. You’ll use the options dictionary to set the necessary flags.
// 这里是添加的部分,名如其意,当我们需要自动版本迁移时,我们需要在addPersistentStoreWithType方法中设置如下options
let options = [NSInferMappingModelAutomaticallyOption: true, NSMigratePersistentStoresAutomaticallyOption: true]
var error: NSError? = nil
persistentStoreCoordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: options, error: &error)
重新运行应用程序,这次应该就不会奔溃了。
从现在开始,只需要把新模型设为当前版本并启动启用轻量级迁移,CoreData就会无缝的完成迁移过程。
默认的迁移方式
有时候我们需要比轻量级更为精细的控制手段。比方说,我们要把实体A替换为另外一个实体B,并且还想把A实体中的属性abc迁移到实体B中的xyz属性上面。abc中已有数据也要迁移到xyz属性。为了完成这些需求,开发者需要创建模型映射,以便指明映射关系。在添加持久化存储区时,即便 NSInferMappingModelAutomaticallyOption
选项设置为true,Core Data 也还是会先检测有没有文件,如果有的话,那么在执行自动推断之前,它会先试着使用这个文件来迁移。在测试映射模型之前,建议先禁用该选项,这样才能确定映射模型是不是已经付诸实用并且能够正常运作了。
请按照下列步骤修改xcdatamodel
,以添加新的映射模型:
确保Data Model 处于选中状态
点击 File > New > File ... 菜单项
选择 iOS > Core Data > Mapping Model , 并点击Next 按钮
把 Model2.xcdatamodel 选为Source Data Model ,并点击Next按钮
把 Model3.xcdatamodel 选为Target Data Model ,并点击Next按钮
将mapping model 的名称设为Model2toModel3 ,并将其保存
选中
Model2toModel3.xcmappingmodel
现在你将看到如下图的界面:
Xcode 目前呈现出来的这套映射是Coredata 以最合理的方式推断出来的。在界面的左方,会看到 ENTITY MAPPINGS
字样,它下面列出了源实体与目标实体之间的映射。实体映射时所采用的命名标准是SourceToDestination (源实体命名到目标实体命名)。有时候就会出现某一个实体并没有与之对应的源实体,可能是应为没有出现在源模型里。这时你需要手动映射。
在上图的右侧你可以看到各种设置。
如果要实现更为复杂的迁移方式,那么可以在 Custom Policy 文本框中输入类名,这个类应该是NSEntityMigrationPolicy
的子类。该子类中,可以通过复写createDestinationInstancesForSourceInstance
方法而操作待迁移的数据。比方说,可以拦截abc这个属性的值,将其中每个单词的首字母改为大写,然后再把修改过的值迁移到xyz 属性。
底部的Source Fetch选项可以通过谓词(Filter Predicate 文本框中输入)限定迁移过来的数据量。假如只想把旧数据中的一部分迁移过来,那么这个选项就很有用。此处的谓词格式与通常代码中编写的谓词相似,只不过要用 $source
变量来表示源数据。比方说,如果把abc属性值为 nil 的源数据排除掉,那么谓词可以写成 $source.abc != nil
。
选定ENTITY MAPPINGS
字样下方的ItemToItem
实体,并观察属性映射内容,会看到目标实体中的每个属性都设置对应的Value Expression
当你要升级你的数据模型到新版,你将先选择一个基准模型。对于轻量级迁移,持久化存储会为你自动推断一个映射模型。然而,如果你对新模型所做的修改并不被轻量级迁移所支持,那么你就需要创建一个映射模型。一个映射模型需要一个源数据模型和一个目标数据模型。 NSMigrationManager 能够推断这两个模型间的映射模型。这使得它很诱人,可用来一路创建每一个以前的模型到最新模型之间的映射模型,但这很快就会变成一团乱麻。对于每一个新版模型,你需要创建的映射模型的量将线性增长。这可能看起来不是个大问题,但随之而来的是测试这些映射模型的复杂度大大提高了。
想像一下你刚刚部署一个包含版本 3 的数据模型的更新。你的某个用户已经有一段时间没有更新你的应用了,这个用户还在版本 1 的数据模型上。那么现在你就需要一个从版本 1 到版本 3 的映射模型。同时你也需要版本 2 到版本 3 的映射模型。当你添加了版本 4 的数据模型后,那你就需要创建三个新的映射模型。显然这样做的扩展性很差,那就来试试渐进式迁移吧。
通过迁移管理器来迁移数据
除了通过NSPersistentStoreCoordinator
来迁移存储区之外,还可以通过采用迁移管理器来做。迁移管理器可以使开发者全权掌握迁移过程中创建的文件,从而令他们能够按自己的方式来灵活处理迁移中的种种问题。使用迁移管理器的一个好处就是可以向用户报告迁移进度,使用户知道应用程序哪次会启动的比较慢些,所以需要耐心等待。虽说迁移过程理应执行的非常快才对,但当数据库比较大、变化比较复杂时,迁移过程就需要耗费一定的时间了。为了使用户界面保持流畅,迁移过程必须在后台线程里执行。只有这样做,用户界面才能反映灵敏,并能把最新动态提供给用户。实现数据迁移的难点在于如何防止用户在迁移的过程中操作应用程序。由于此时数据尚未准备好,所以我们必须做这个限制,否则用户就会对着黑屏幕不知所措。
未完成..
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。