使用 Core Data 时的模型实例化问题

Posted

技术标签:

【中文标题】使用 Core Data 时的模型实例化问题【英文标题】:Model instantiation question when using Core Data 【发布时间】:2009-08-04 01:08:53 【问题描述】:

我对 Core Data 的一个方面有些困惑。也就是说,我什么时候使用基本的 alloc/init 例程,而不是使用核心数据创建一个对象并将其保存到当前托管对象上下文中。

我知道这是一个相当模糊的问题,所以让我举个例子。

我目前正在开发一个应用程序,它会遍历 iPhone 上用户的所有通讯录。从那里,我编写了一个名为“Person”的模型类。我曾经在一群人中做过这样的事情:

Person *person = [[Person alloc] initWithWrapper:mywrapper];

mywrapper 将包含一个带有 person 属性的 NSDictionary。稍后我就可以在我的应用程序中使用人员对象填充通讯录。

现在我已经开始使用 Core Data 重建应用程序的某些部分。我是否继续使用上述策略来填充我的通讯录?或者我是否可以这样做:

    Person *person = (Person *)[NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:managedObjectContext];

    [person setName:name];
    [person setDob:dob];

    // Commit the change.
    NSError *error;
    if (![managedObjectContext save:&error]) 
        // Handle the error.
    

问题是,每次应用启动时都会执行此代码。我是否应该不使用核心数据,因为它会在每次加载应用程序时使用冗余人员实例填充存储机制?我应该修改我的 NSManagedObject(Person 类)并添加我的 initWithWrapper: 方法并像往常一样继续吗?

有点困惑,希望澄清。

【问题讨论】:

【参考方案1】:

您永远不应该在托管对象上下文之外初始化 Core Data 对象——这根本没有意义。有一些

Person *person = [[[Person alloc] init] autorelease];

在没有上下文(因此模型和存储协调器)支持的情况下,您无法保存对象、操作它或真正执行 Core Data 提供的任何有用的操作,这对您没有好处。

当您第一次将对象插入Core Data 时,您应该只使用alloc-init 组合;这就是initWithEntity:insertIntoManagedObjectContext: 方法的用途。你是对的,每次调用该方法时,你都是在向 Core Data 上下文中插入一个新对象并因此存储,如果你不小心,你可能会得到重复的对象。

如果您在每次启动时都运行代码,我会向您推荐的方法是提出一个核心数据查询,该查询返回一组现有的 Person 对象,并且只添加对象(使用初始化方法)商店中尚不存在。如果对象已经存在,请修改它而不是创建一个新对象。

诀窍是让这样的事情正常执行。你不应该对 iPhone 通讯录中的每个联系人都进行核心数据提取;许多像这样的小东西非常昂贵。理论上你可以得到两个 NSSet——一个是 Person 对象,一个是一个联系人——然后通过一些唯一的键(比如联系人的名字和姓氏的哈希)来比较它们。我把优化留给你。

关键是:不要在 Core Data 对象上使用 allocinit,除非您是第一次将该对象插入上下文中。而是查看现有对象并在必要时对其进行修改。

【讨论】:

或者除非你重写init并调用super的initWithEntity:insertIntoManagedObjectContext:方法。 =) 如果有人无法确定重写 init 的原因,正如上面提到的 Dave,我刚刚找到了一个:ObjectiveResource(以及可能其他非核心数据感知的依赖项)可能会尝试实例化您的类您(例如,在取消编组服务器响应时)。这意味着您可能需要重写 init 来解决这个确切的问题。【参考方案2】:

是的,将 initWithWrapper 方法添加到您的 Person 类是最简单的。应该是这样的:

- (id) initWithWrapper:(NSDictionary *)wrapper 
  NSEntityDescription * person = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:someMOC];
  if (self = [super initWithEntity:person insertIntoManagedObjectContext:someMOC]) 
    //do your wrapperly initialization here
  
  return self;

唯一的缺点是这个方法必须知道它应该将对象插入到哪个 managedObjectContext 中,所以你必须想办法提供它。

话虽如此,我自己一直都在使用这种模式。

【讨论】:

所以每次应用程序启动时,您仍然将人员对象添加到 managedObjectContext 中吗?如果它总是相同的人对象每个开始?这不会在数据存储中重复 Person 对象吗? @Coocoo4Cocoa 为什么不直接执行一个带有“name = %@”谓词的 fetch 请求,并在添加新人之前查看它是否返回任何结果?

以上是关于使用 Core Data 时的模型实例化问题的主要内容,如果未能解决你的问题,请参考以下文章

[Vue]实例化Vue时的两种挂载方式el与$mount

使用 Java 8 流时的新对象实例化

核心数据:使用延迟实例化时的奇怪错误行为

何时版本化 Core Data 模型

如何在 EF Core 中实例化 DbContext

实例化领域对象时的 EXC_BAD_INSTRUCTION