正确实现父/子NSManagedObjectContext

我的应用程序有时会将对象插入托pipe对象上下文中,而这些对象并不一定要保存。 例如,当我启动“添加实体”模式时,我创build一个托pipe对象并将其分配给模式。 如果用户保存该模式,我保存上下文。 如果他取消,我删除对象,不需要保存。

我现在介绍了一个“导入”function,切换到我的应用程序(使用URLscheme),并添加一个实体。 因为这些模式中的一个可能是开放的,所以在这一点上保存上下文是不安全的。 为模式创build的临时对象将被保存,即使用户取消,也不能保证删除(从取消操作)将被保存 – 用户可能会退出该应用程序。

同样,我不能简单地保存每当我的应用程序退出。 如果模态在此时打开,临时对象将被错误地保存。

为了解决这个问题,我正在试图使用一个子的上下文,正如这里所讨论的。 读完所有我能find的,我有几个问题:

  1. 我应该为每个上下文使用哪种并发types? 请记住,我没有这样做的性能/线程的好处。 我知道我不能使用NSConfinementConcurrencyType的主要上下文,如果它有子上下文,但我不知道哪两个选项是最适合的。 对于儿童情况,它是否需要匹配? 或者我甚至可以在这里使用限制types? 我已经尝试了各种组合,都似乎工作正常,但我想知道哪些是适合我的要求。

  2. (侧面问题)为什么我只能使用这个工作,如果我使用一个类的iVar? 我认为我应该能够在创build它的方法中声明临时上下文,然后使用entity.managedObjectContext引用它。 但是,当我来访问它似乎是零? 如果我使用一个iVar来保存参考,这是纠正。

  3. 什么是正确的方式或传播到主要背景的变化? 我已经看到在每个上下文中使用不同的块封装实现的各种注释。 它取决于我的并发types? 我目前的版本是:

    //save the new entity in the temporary context NSError *error = nil; if (![myObject.managedObjectContext save:&error]) {NSLog(@"Error - unable to save new object in its (temporary) context");} //propogate the save to the main context [self.mainContext performBlock:^{ NSError *error2 = nil; if (![self.mainContext save:&error2]) {NSLog(@"Error - unable to merge new entity into main context");} }]; 
  4. 当我的用户保存,它发送它的委托(我的主视图控制器)消息。 委托传递被添加的对象,并且它必须在主要上下文中find相同的对象。 但是,当我在主要环境中寻找它时,却找不到它。 主要的上下文包含实体 – 我可以logging它的细节并确认它在那里 – 但地址是不同的? 如果这是为了发生(为什么?),如何在保存后在主上下文中find添加的对象?

感谢您的任何见解。 对不起,有一个很长的问题,但我想以前有人可能已经解决了所有这些问题。

家长/孩子MOC模型是Core Data的一个非常强大的function。 它简化了我们不得不处理的古老的并发问题。 但是,正如你所说的,并发不是你的问题。 回答你的问题:

  1. 传统上,对于与主线程关联的NSManagedObjectContext使用NSMainQueueConcurrencyType ,对于子上下文使用NSPrivateQueueConcurrencyType 。 子上下文不需要匹配其父。 如果不指定types,则NSConfinementConcurrencyType是所有NSManagedObjectContext的默认值。 基本上是“我将pipe理自己的核心数据线程”types。
  2. 在没有看到你的代码的情况下,我的假设就是你创build子上下文的范围,并且它被清除。
  3. 在使用父/子上下文模式时,您需要使用块方法。 使用块方法的最大好处是操作系统将把方法调用分派给正确的线程。 您可以使用performBlock进行asynchronous执行,或者使用performBlockAndWait进行同步执行。

你会使用这个如:

 - (void)saveContexts { [childContext performBlock:^{ NSError *childError = nil; if ([childContext save:&childError]) { [parentContext performBlock:^{ NSError *parentError = nil; if (![parentContext save:&parentError]) { NSLog(@"Error saving parent"); } }]; } else { NSLog(@"Error saving child"); } }]; } 

现在,您需要记住,在保存之前,在子上下文中进行的更改(例如插入的实体)将不可用于父上下文。 对于子上下文,父上下文是持久存储。 保存时,将这些更改传递给父代,然后可以将其保存到实际的持久性存储中。 保存传播一级。 另一方面,进入一个儿童环境将把数据从各级(通过家长进入孩子)

  1. 您需要在managedObjectContext上使用某种forms的objectWithID 。 它们是在上下文之间传递对象的最安全(也是唯一)的方法。 正如Tom Harrington在评论中提到的,你可能想使用existingObjectWithID:error:虽然因为objectWithID:总是返回一个对象,即使你传递了一个无效的ID(这可能导致exception)。 欲了解更多详情: 链接

我有类似的问题,这里有一些问题的答案 – 1.你应该能够使用并发typesNSPrivateQueueConcurrencyTypeNSMainQueueConcurrencyType 2.假设你已经创build了一个父上下文mainContext的临时上下文tempContext (假设iOS5)。 在这种情况下,您可以将您的托pipe对象从tempContext移动到mainContext by-

 object = (Object *)[mainContext objectWithID:object.objectID]; 

然后你可以保存mainContext本身。

也许还有,

 [childContext reset]; 

如果你想重置临时的上下文。

  1. 如果使用父/子模式,则通常使用NSMainQueueConcurrencyType和具有NSMainQueueConcurrencyType的子上下文声明父上下文。 NSConfinementConcurrencyType用于传统的线程模式。

  2. 如果你想保持上下文,你需要一个强有力的参考。

  3. 您只需在子上下文中调用save方法将更改推送到父上下文,如果要保留数据,还可以在父上下文中调用save。 你不需要在一个块内做到这一点。

  4. 有几种方法可以从上下文中获取特定的对象。 我不能告诉你哪一个会在你的情况下工作,尝试一下:

    - objectRegisteredForID:

    - objectWithID:

    - existingObjectWithID:error: