【问题标题】:Swift - Coredata Migration - Set new attribute value according to old attribute valueSwift - Coredata Migration - 根据旧属性值设置新属性值
【发布时间】:2016-11-17 06:06:18
【问题描述】:

我当前的一个核心数据实体 - Entity1 - 有一个名为 isSavedBoolean 属性。

在新的核心数据模型中,我计划删除isSaved 属性并添加一个名为type 的新Int 属性。对于所有保存的Entity1对象,我想根据旧核心数据模型中isSaved的值来设置type的值。 (例如,如果 isSaved 为 true,则 type 为 1,否则 type 为 2)。

我读过一些关于轻量级核心数据迁移的文章,但似乎都没有帮助。

只是想知道是否有任何方法可以使我计划的迁移工作?

【问题讨论】:

    标签: ios swift core-data


    【解决方案1】:

    轻量级迁移无法做到这一点。您必须创建一个映射模型和NSEntityMigrationPolicy 的子类。这并不难,但对于大多数 iOS 开发人员来说,这是一个陌生的领域。步骤如下:

    1. 创建映射模型。在 Xcode 中,文件 --> 新建 --> 映射模型。当您单击“下一步”时,Xcode 将询问此映射的源(旧)和目标(新)模型文件。
    2. 模型文件将在可能的情况下推断映射。其他一切都将是空白的。使用您的 type 和其他一些属性,它将如下所示。 $source.timestamp 之类的条目表示复制迁移前的现有值。

    1. 创建NSEntityMigrationPolicy 的新子类。给子类起一个明显的名字,比如ModelMigration1to2。这个类将告诉 Core Data 如何将旧的布尔值映射到新的整数值。

    2. 向子类添加一个方法来转换值。类似于以下内容。方法名称无关紧要,但如果您选择描述性的名称会很好。您需要在这里使用 ObjC 类型——例如NSNumber 而不是 IntBool

       @objc func typeFor(isSaved:NSNumber) -> NSNumber {
           if isSaved.boolValue {
               return NSNumber(integerLiteral: 1)
           } else {
               return NSNumber(integerLiteral: 2)
           }
       }
      
    3. 返回映射模型并告诉它使用您的子类作为其自定义映射策略。那是在“自定义政策”下右侧的检查器中。请务必包含模块名称和类名称。

    1. 告诉映射模型使用您之前创建的函数从旧的isSaved 属性中获取type 属性的值。下面说在名为typeForIsSaved:: 很重要)的自定义策略类上调用一个函数,并带有一个参数,并且该参数应该是$source(旧托管对象)上的isSaved 值。

    迁移应该现在可以工作了。您不必告诉 Core Data 使用映射模型——它会发现需要迁移并寻找与新旧模型版本匹配的模型。

    几点说明:

    • 如果您因类似 Couldn't create mapping policy for class named... 的错误而崩溃,那么您在第 5 步中忘记了上面的模块名称(或弄错了)。
    • 如果您因unrecognized selector 错误而崩溃,则步骤4 中的方法签名与您在步骤6 中输入的内容不匹配。如果您忘记在函数声明中包含@objc,也会发生这种情况。

    【讨论】:

    • 非常感谢汤姆如此详细的回答。刚刚试了一下,效果就像一个魅力!除了第 6 步中的方法签名需要更改为“typeFor:”。如果您可以更新第 6 步的图像,那就太好了。
    • 截图和方法签名是我在一个工作测试项目中使用的。如果我在方法中设置断点,我可以验证它是否在迁移期间被调用。你有什么问题,你做了什么来解决它?
    • 我将它从“typeForIsSaved:”更改为“typeFor:”,否则我会收到无法识别的选择器错误。顺便说一句,我使用的是 Xcode 7.3。
    • 我实现了这一点,然后测试了从旧数据库的迁移,该旧数据库不在新数据库之前的版本上。所以,我正在从模式 v25 迁移到 v26,但核心数据存储说是在新版本启动时在 v24 上。该模型已迁移到 v26,例如存在新属性,但未发生自定义迁移。我现在正在阅读 Core Data 不支持开箱即用的渐进式迁移,而是尝试推断映射模型,忽略此手动映射。添加渐进式迁移似乎很复杂。我是否正确阅读了情况?
    • "typeForIsSaved" 和 "typeFor" 似乎不再起作用了。根据this answer,它应该是“typeForWithIsSaved”
    【解决方案2】:

    使用 Xcode 9.1 Beta 和 Swift 4,我发现迁移是可行的,但你必须小心如何指定转换方法名称,而且似乎你需要将你的函数标记为 @objc。

    比如我的值表达式:

    FUNCTION($entityPolicy, "changeDataForData:" , $source.name)
    

    我的转换策略方法名称:

    class StudentTransformationPolicy: NSEntityMigrationPolicy {
      @objc func changeData(forData: Data) -> String {
          return String(data: forData, encoding: .utf8)!
      }
    }
    

    在模型更改后启动我的应用程序时,我在触发它之前确实很棘手并且进行了大量试验。如果所有这些都不起作用,为您的策略实施“createDestinationInstances”可能会更容易,但我们将把它留到另一天......

    【讨论】:

    • 您可以通过将签名更改为 @objc func changeData(forData data: Data) -> String { return String(data: data, encoding: .utf8) 来使它更好一点! }
    猜你喜欢
    • 2022-09-23
    • 2020-10-11
    • 1970-01-01
    • 2016-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-06
    • 1970-01-01
    相关资源
    最近更新 更多