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。我知道。以下是实体 Choice 和 ChoiceType 的示例。
@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 用作NSString 或NSSet,编译器会报错。它也会很有帮助,并在我输入时建议 NSNumber 方法作为完成。
逻辑如何?
这种方法不提供您添加到实体类的逻辑。
在 Swift 中,您也许可以使用协议扩展来实现这一点。
我正在停用这些实体(因此进行了迁移),因此我将实体逻辑函数 我需要 移到了我的自定义迁移的辅助函数中。例如,choice.orderedChoiceTypes 变为 [self choiceOrderedChoiceTypes:choice]。
将来我可能会避免向NSManagedObject 实体添加逻辑。我认为将任何此类逻辑放在从托管对象构建的域对象中可能是一个更好的计划。此外,我可能会避免定义实体类,而是在迁移期间通过协议仅访问NSManagedObject 实例。它看起来干净、简单、消除了魔法,并且有利于测试——而不仅仅是迁移。