【问题标题】:No Core Data entity subclasses during custom migrations自定义迁移期间没有核心数据实体子类
【发布时间】:2017-02-07 21:04:27
【问题描述】:

我有一个核心数据数据库。我正在执行自定义迁移。我有一个NSEntityMigrationPolicy 的子类。我的策略迁移方法buildValueFromSectionFieldManagedObject: 被映射规则调用:FUNCTION($entityPolicy, "buildValueFromSectionFieldManagedObject:" , $source)

这部分确实有效。

但是,buildValueFromSectionFieldManagedObject: 的实现使用$source 的自定义实体NSManagedObject 子类中的方法,即Choice

Choice 的方法似乎不适用于迁移功能,而它只是一个普通的NSManagedObject

当我尝试使用 Choice 方法时,我得到一个异常。如果我在调试器中选择po,我会得到这样的结果:

NSManagedObject:0x600000281860>(实体:Choice;id:0xd00000000038001a;数据:)

然而,在迁移过程中我通常会看到这样的内容:

选择:0x60800028bdb0>(实体:选择;id:0x6080002225a0;数据:{

是这样吗,还是有什么方法可以在迁移过程中使用实体对象?

可能相关 - 此特定实体 Choice 在此迁移期间被删除。它在目标托管对象模型中不存在,但在源托管对象模型中确实存在。但是,我认为情况并非如此,因为目标模型中的其他实体类在迁移期间也不能作为该类使用 - 它们具有类 NSManagedObject,并且它们的实体方法不可用。

【问题讨论】:

    标签: core-data migration


    【解决方案1】:

    没错,迁移期间您只能访问基本的NSManagedObjects。

    三阶段迁移

    迁移过程本身分为三个阶段。它使用源模型和目标模型的副本,其中禁用了验证规则,并且所有实体的类都更改为 NSManagedObject。

    发件人:Core Data Model Versioning and Data Migration Guide

    【讨论】:

    • 谢谢。 …我认为回想起来,在核心数据中包含实体对象并不是一个好计划。它似乎鼓励了糟糕的架构(托管对象渗透到视图控制器)。我为解决这种迁移而采取的方法是定义协议来公开迁移所需的(旧版)接口。在迁移过程中,我将我的 NSManagedObject 实例转换为 id <TheCorrectProtocol> 给我类型安全和财产安全,并且不会误导我对象是什么。我认为这通常比实体类更可取——哦,好吧。
    • 我确实让它工作了,我对结果很满意......所以,我相信即使是香草NSManagedObject 实现respondsToSelector: 所以你可以读/写它的属性你会是一个普通的对象,不需要使用setValue:forKey:。我将添加另一个推荐和参考。
    • 我可能会添加第二个答案来显示解决方案,因为这似乎是一个相当不错的方法,恕我直言。非常感谢你证实了我最初的怀疑!
    • 啊,我想你的子类中可能有更复杂的逻辑,你想从迁移中访问。回想起来,一个简单的协议适合你是有道理的。我肯定会发布另一个答案,详细说明您如何处理您的问题。
    • 你说得对,我确实在子类中有更复杂的逻辑。我能够将此因素考虑到迁移助手中。并且拥有协议使助手更容易开发(键入时代码完成)和更安全(发送错误消息时的警告/错误)。完成此操作后,我的感觉是,这通常是与核心数据对象交互的更好方法。这意味着你更少依赖它的“魔法”,并把它当作它本来的样子。如果你需要魔法,那么你写魔法并理解它的局限性——所以它不是魔法:-)
    【解决方案2】:

    Dave's answer 澄清说,在迁移过程中,核心数据对象只能作为NSManagedObject 实例使用。你不能使用他们的实体类。

    更糟糕的是,如果您使用像 mogenerator 这样的工具,那么您扩展实体类所使用的任何方便的逻辑都无法访问。

    糟糕的解决方案

    直接使用NSManagedObject 对我来说很危险。使用[managedObject valueForKey:@"someKey"] 很冗长,但更糟糕的是,没有编译器检查您的键名是否正确,因此您可能会要求managedObject 没有的东西。也没有编译器检查返回的类型 - 它可以是您可以放入托管对象的任何内容。

    稍微好一点的是[managedObject valueForKey: NSStringFromSelector(@selector(someKey))] 更安全,但非常冗长和尴尬,而且仍然不是那么安全 - 很多事情可能会实现方法someKey

    您也可以将键声明为文字:

    NSString *const someKey = @"someKey";
    [managedObject valueForKey: someKey];
    

    再说一遍——这更安全一些,更不容易出错和冗长,但你仍然不能保证 this 托管对象有someKey

    这些方法都不能让我们访问自定义逻辑。

    更好的解决方案

    我所做的而不是为我的托管对象所具有的属性定义一个protocol我知道。以下是实体 ChoiceChoiceType 的示例。

    @protocol ChoiceProtocol
    - (NSSet<id <ChoiceTypeProtocol> > *)choiceTypes;
    - (NSNumber *)selected;
    - (NSNumber *)order;
    @end
    
    @protocol ChoiceTypeProtocol
    - (NSNumber *)selected;
    - (NSString *)name;
    - (NSString *)textCustom;
    - (NSNumber *)order;
    @end
    

    现在在我的迁移代码中,而不是具有类似于以下的自定义迁移功能:

    - (NSString *)migratedRepresentationOfChoice:(NSManagedObject *)choice;
    

    我有:

    - (NSString *)migratedRepresentationOfChoice:(id <ChoiceProtocol>)choice;
    

    在这个函数的主体中,我可以像使用任何常规对象一样使用choice。我在键入时得到代码完成,我得到正确的语法突出显示,如果我调用不存在的方法,编译器会抱怨。

    我还检查了返回类型,因此如果我将NSNumber 属性selected 用作NSStringNSSet,编译器会报错。它也会很有帮助,并在我输入时建议 NSNumber 方法作为完成。

    逻辑如何?

    这种方法不提供您添加到实体类的逻辑。

    在 Swift 中,您也许可以使用协议扩展来实现这一点。

    我正在停用这些实体(因此进行了迁移),因此我将实体逻辑函数 我需要 移到了我的自定义迁移的辅助函数中。例如,choice.orderedChoiceTypes 变为 [self choiceOrderedChoiceTypes:choice]

    将来我可能会避免向NSManagedObject 实体添加逻辑。我认为将任何此类逻辑放在从托管对象构建的域对象中可能是一个更好的计划。此外,我可能会避免定义实体类,而是在迁移期间通过协议访问NSManagedObject 实例。它看起来干净、简单、消除了魔法,并且有利于测试——而不仅仅是迁移。

    【讨论】:

    • 非常彻底的答案!
    • 干杯,感谢您指出正确的方向@DaveWeston
    猜你喜欢
    • 2013-04-19
    • 2018-12-05
    • 1970-01-01
    • 1970-01-01
    • 2018-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-21
    相关资源
    最近更新 更多