【问题标题】:Inheritance Core Data: Do I need to specify a Subclass in my xcdatamodel?继承核心数据:我需要在我的 xcdatamodel 中指定子类吗?
【发布时间】:2015-03-05 00:11:23
【问题描述】:

我有一个名为 Mail 的基类,它本质上将充当一个抽象类,它具有确定邮件主题、正文、发件人等内容的具体子类。

让我们称一个这样的具体子类 NewsMail。

我之前在 Obj C 中设置过多态关系,但从未在 CoreData 中设置过。

似乎以下行需要在 xcdatamodel 中声明的类。以下行:

[NewsMail MR_createInContext:[NSManagedObjectContext MR_context]];

产生这个错误:

"NSInternalInconsistencyException", "+entityForName: could not locate an entity named 'NewsMail' in this model.

现在我可以通过在我的 xcdatamodel 中为我拥有的每个具体子类添加一个实体来解决这个问题,但这会在一段时间后变得笨拙。

建议?

更新

所以我有一个类别“邮件+类型”,我打算在其中配置具体类型:

#import "Mail.h"
#import "MailProtocol.h"

@interface Mail (Types)

+ (instancetype)newInstanceInContext:(NSManagedObjectContext*)context;

@end

@interface WelcomeMail : Mail<MailProtocol> @end

MailProtocol 将定义具体邮件应遵循的附加方法:

#import <Foundation/Foundation.h>

@protocol MailProtocol <NSObject>

- (NSString*)subjectKey;
- (NSString*)bodyKey;
- (void)build;

@end

(instancetype)newInstanceInContext:(NSManagedObjectContext*)context; 方法应该返回一个具体的类,但从父邮件类构建,如下面的答案中所指出的:

+ (instancetype)newInstanceInContext:(NSManagedObjectContext*)context
{
    return [NSEntityDescription insertNewObjectForEntityForName:@"Mail" inManagedObjectContext:context];
}

我可以确认newInstanceInContext的正确具体类实现被执行了。

不幸的是,如果我尝试在 WelcomeMail 上运行构建,我会收到“无法识别的选择器”错误,因为它尝试在父级“邮件”类上运行它。

【问题讨论】:

  • I gave up。它笨重的。可能有一种以编程方式编辑 MOM 的方法,但这同样丑陋。
  • 在下面查看我的附加评论。这是我的错。显然,初始化程序会返回一个 Mail。我在下面建议了其他几种方法 - 但您可能会发现这些方法的开销或风险不仅仅是在 MOMD 中使用继承的实体。你有点反对 Liskov Substitution,因为你希望一个基类能够代替一个子类——但它真的不应该朝那个方向发展。 Mail 应该能够代替 ExpressMail - 但反过来不行。至少不是没有很多额外的开销。
  • 嗯,也许我没有正确解释自己,但我真正想做的就是让 Mail 子类有自己的实现和属性配置,而无需在 MOM 中输入。如果我从问题中删除核心数据,那么它的行为很完美,但不幸的是我需要持久性......

标签: objective-c inheritance core-data polymorphism magicalrecord


【解决方案1】:

你可以做到,但我怀疑不是你想要的方式。我不使用 MR,但很明显它有一个工厂方法来创建基于类名的托管对象 - 因为你没有具有该名称的实体,所以它失败了。

我所做的是在 MOM 编辑器中创建了两个实体,并将例如 Mail 标记为抽象,然后将每个子类添加为从 Mail 派生的实体。不确定要对每个派生类做什么,因为这是一个很大的负担。毕竟,每个派生类只需要类需要的附加属性,这些属性不是从 Mail 继承的。包括例如瞬态属性,这些属性可用于通过添加不属于存储模型的行为来添加业务逻辑。

我经常做的另一件事是从代码中的托管对象继承——模型不存储在 MOM 中——以添加额外的逻辑。但在那种情况下,我通常会自己添加一个工厂方法来实例化实际的 MOM 实体。我怀疑在这种情况下,MR 可能试图通过使用错误的实体创建 MO 来帮助您。

只是详细说明一下。我通常会创建一个未存储在 Core Data 中的“基础”模型对象,如下所示:

@interface BaseModel : NSManagedObject

+ (instancetype)newInstanceInContext:(NSManagedObjectContext*)context;

@end


@implementation BaseModel

+ (instancetype)newInstanceInContext:(NSManagedObjectContext*)context
{
   return [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([self class]) inManagedObjectContext:context];
}
@end

我的实际模型对象,比如 Mail(在 MOM 中定义)继承了 BaseModel。

然后我进一步从 Mail 派生另一个类。说快递。 ExpressMail 继承 Mail。但是用:

覆盖newInstanceInContext
+ (instancetype)newInstanceInContext:(NSManagedObjectContext*)context
{
   return [NSEntityDescription insertNewObjectForEntityForName:@"Mail" inManagedObjectContext:context];
}

基本上它会创建一个邮件 MO。然后我可以向 ExpressMail 添加额外的逻辑。很明显,我无法在此处添加任何将在 Mail 中保留的属性。

不确定这是否是您想要的,但它是我经常使用的一种模式来做您想做的事情。

更新

在几个评论线程后更新。我想我们都意识到向下铸造是行不通的。如果你真的只想添加行为——即添加可以装饰或在你的基类上工作的方法——邮件——那么类别肯定可以工作。如果你真的只需要你的 POMO(普通的旧邮件对象),那么你可以使用类别来装饰它/以不同的方式配置它——但如果你真的需要使用不同的子类,那么你必须在你的MOMD 让核心数据能够为您正确地实例化它们。

最后,最后的建议。不确定您是否熟悉名为 MoGenerator 的工具?它会生成一组匹配的类——一个代表您的实际 MOM 类的私有类和另一个可以使用附加逻辑扩展的公共类。无论如何,它看起来有一个非常聪明的方法来用内部私有版本包装你的实体——它创建为 _Mail——然后公开一个公共版本的邮件——然后你可以修改它。所有管道都在 _Mail 类中。 (它还负责包装的苦差事)。我想你也许可以用它来做你想做的事。即生成你的模型——然后创建另一个继承自_Mail 的ExpressMail 子类。再说一次,我不能确定也不能确定它是否消除了向下转换的问题 - 但也许值得一试?

不管怎样,看看吧: https://github.com/rentzsch/mogenerator

后记

我刚刚使用 Mogenerator 自己尝试过。而且您仍然无法将任何内容添加到基础上不存在的 _baseclass 的不同子类中。也就是说,我不能将它生成的 _Mail 类(映射到 MOMD 中的 Mail 实体)子类化——以创建例如 ExpressMail——并添加任何不在 Mail 上的行为。和以前一样的问题。所以看之前的结论。由于已经给出的某些原因,这似乎是不可能的。

【讨论】:

  • 感谢您的回答。我喜欢你的方法,但是当我尝试调用 ExpressMail 上的方法时,它调用 Mail 类而不是 ExpressMail 上的方法。我已经按照你采取的步骤...
  • 也许一些代码可以帮助查看发生了什么?另外,将您的其他方法添加为类别怎么样?
  • 没问题,让我现在更新问题。在添加类别方面,我看不出这有什么帮助,因为任何方法调用要么转到 Mail 实现,要么只是抛出“未找到方法”错误......
  • 对不起 - 我明白了。几个想法 - 除了初始化器之外,在返回之前还向下转换为您的 ExpressMail 类型怎么样?我认为这会奏效——但我不喜欢它。您还可以使用组合 - 而不是 ExpressMail 作为邮件 - 它可以创建并拥有一个。当然,使用这种方法,您必须将方法调用从 EMail 代理到 Mail。但我认为可行。
  • 对不起。刚刚在代码中尝试了自己 - 看看你的意思。因此,采用向下铸造的方法-无论如何这都很糟糕。我认为你在 MOMD 中留下了组合或继承。当然,除非其他人有更好的主意。祝你好运!
猜你喜欢
  • 2019-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-11
  • 1970-01-01
  • 2013-04-06
相关资源
最近更新 更多