我的应用有时会将对象插入到托管对象上下文中,而这些对象并不一定要保存。例如,当我启动“添加实体”模式时,我创建了一个托管对象并将其分配给模态。如果用户从该模态中保存,我将保存上下文。如果他取消,我删除该对象,不需要保存。
我现在已经引入了一个'导入'功能,可以切换到我的应用程序(使用URL方案)并添加一个实体。由于其中一个模态可能是打开的,因此此时保存上下文是不安全的。即使用户取消,也会保存为模态创建的瞬态对象,并且无法保证稍后将保存删除(来自取消操作) - 用户可能会退出该应用程序。
同样,每当我的应用程序退出时,我都无法保存。如果模式在该点打开,则临时对象将被错误地保存。
为了解决这个问题,我试图使用子上下文,正如here所讨论的那样。阅读了所有我能在SO上找到的内容,我会提出几个问题:
我应该为每个上下文使用哪种并发类型?请记住,我没有这样做是为了获得性能/线程优势。我知道如果要有子上下文,我不能使用NSConfinementConcurrencyType作为主上下文,但我不确定其他两个选项中哪一个最适合。对于子上下文,它是否需要匹配?或者我甚至可以在这里使用限制类型?我尝试了各种各样的组合,似乎一切正常,但我想知道哪个适合我的要求。
(附带问题)如果我使用iVar类,为什么我只能让它工作?我以为我应该能够在创建它的方法中声明临时上下文,然后使用entity.managedObjectContext来引用它。但是当我访问它时它似乎是零?如果我改为使用iVar来保存参考,那么这已得到纠正。
将更改传播到主要上下文的正确方法是什么?我已经在每个上下文中看到了使用不同的块包装实现的各种注释。它取决于我的并发类型吗?我目前的版本是:
//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");}
}];
当我的用户保存时,它会向其委托(我的主视图控制器)发送一条消息。委托传递添加的对象,它必须在主上下文中找到相同的对象。但是当我在主要环境中寻找它时,却找不到它。主要上下文确实包含实体 - 我可以记录其详细信息并确认它在那里 - 但地址是不同的?如果要发生这种情况(为什么?),如何在保存后在主上下文中找到添加的对象?
感谢您的任何见解。很抱歉有一个很长的,多部分的问题,但我认为有人可能已经解决过所有这些问题。
答案 0 :(得分:46)
父/子MOC模型是核心数据的一个非常强大的功能。它简化了我们以前必须处理的古老并发问题。但是,正如您所说,并发不是您的问题。回答你的问题:
NSMainQueueConcurrencyType
作为与主线程相关联的NSManagedObjectContext
,并使用NSPrivateQueueConcurrencyType
作为子上下文。子上下文不需要与其父上下文匹配。如果您未指定类型,则NSConfinementConcurrencyType
是所有NSManagedObjectContext
默认的内容。它基本上是“我将管理我自己的Core Data线程”类型。performBlock
进行异步执行,或使用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");
}
}];
}
现在,您需要记住,在您保存之前,子上下文中所做的更改(例如插入的实体)将无法用于父上下文。对于子上下文,父上下文是持久性存储。保存时,将这些更改传递给父级,然后父级可以将它们保存到实际的持久性存储中。将propogate更改保存在一个级别。另一方面,获取子上下文会将数据向下拉到每个级别(通过父级和子级)
objectWithID
。它们是在上下文之间传递对象的最安全(也是唯一的)方法。正如Tom Harrington在评论中提到的那样,您可能希望使用existingObjectWithID:error:
,因为objectWithID:
总是返回一个对象,即使您传入了无效的ID(可能导致异常)。有关详细信息:Link 答案 1 :(得分:6)
如果您使用父/子模式,通常会使用NSMainQueueConcurrencyType
声明父上下文,并使用NSPrivateQueueConcurrencyType
声明子上下文。 NSConfinementConcurrencyType
用于经典线程模式。
如果你想保留上下文,你需要以某种方式强烈引用它。
您只需在子上下文中调用save方法将更改推送到父上下文,如果要保留数据,也可以在父上下文中调用save。你不需要在一个街区内这样做。
有几种方法可以从上下文中获取特定对象。我不能告诉你哪一个适用于你的情况,试试看:
- objectRegisteredForID:
- objectWithID:
- existingObjectWithID:error:
答案 2 :(得分:6)
我遇到过类似的问题,以下是您问题某些部分的答案 -
1.您应该能够使用并发类型NSPrivateQueueConcurrencyType
或NSMainQueueConcurrencyType
2.假设您已创建了具有父上下文tempContext
的临时上下文mainContext
(假设是iOS5)。在这种情况下,您只需将托管对象从tempContext
移动到mainContext
-
object = (Object *)[mainContext objectWithID:object.objectID];
然后您可以保存mainContext本身。
也许,
[childContext reset];
如果要重置临时上下文。