假设我们在核心数据模型中有两个实体:部门和员工。
部门与员工是一对多的关系。
我有以下 ManagedObjectContexts:
- Root:连接到 Persistent Store Coordinator
- 主要:与父根的上下文
当我想创建一个员工时,我执行以下操作:
- 我在主要上下文中有一个部门
- 我在主上下文中创建了一个员工
- 我将部门分配给员工的部门属性
- 我保存主要上下文
- 我保 stub 上下文
这会在 Main 上下文和 Root 上下文中创建一个保留循环。
如果我在没有子上下文的情况下执行此操作(全部在 Root 上下文中),那么我可以通过在 Employee 上调用 refreshObject:mergeChanges
来打破保留周期。在我使用这两个上下文的情况下,我仍然可以使用该方法来打破 Main 上下文的循环,但是我将如何打破 Root 上下文的循环?
旁注:这是一个描述我的问题的简单示例。在 Instruments 中,我可以清楚地看到分配的数量在增长。在我的应用程序中,我的上下文比一层更深,导致了更大的问题,因为我得到了一个新的实体分配,每个上下文都有保留周期,我正在保存。
更新 15/04:NSPrivateQueueConcurrencyType 与 NSMainQueueConcurrencyType
保存两个上下文后,我可以使用 Department 对象在 Main 上下文中执行 refreshObject:mergeChanges
。正如预期的那样,这将重新故障部门对象,打破保留周期并在该上下文中取消分配部门和员工实体。
下一步是打破存在于根上下文中的保留循环(保存主上下文已将实体传播到根上下文)。我可以在这里执行相同的技巧,并在具有 Department 对象的 Root 上下文中使用 refreshObject:mergeChanges
。
奇怪的是:当我的根上下文是使用 NSMainQueueConcurrencyType 创建的(所有分配都重新出错并解除分配)时,这工作正常,但当我的根上下文是使用 NSPrivateQueueConcurrencyType 创建时不起作用(所有分配都重新出错,但 不是 已释放)。
旁注:根上下文的所有操作都在 performBlock(AndWait) 调用中完成
更新 15/04:第 2 部分
当我使用 NSPrivateQueueConcurrencyType 在根上下文上执行另一个(无用,因为没有更改)保存或回滚时,对象似乎已被释放。我不明白为什么这与 NSMainQueueConcurrencyType 的行为不同。
更新 16/04:演示项目
我创建了一个演示项目:http://codegazer.com/code/CoreDataTest.zip
21/04 更新:到达那里
感谢乔迪·哈金斯的帮助!
我正在尝试将 refreshObject:mergeChanges
从我的 ManagedObject didSave
方法中移出。
您能否向我解释一下两者之间的区别:
[rootContext performBlock:^{
[rootContext save:nil];
for (NSManagedObject *mo in rootContext.registeredObjects)
[rootContext refreshObject:mo mergeChanges:NO];
}];
和
[rootContext performBlock:^{
[rootContext save:nil];
[rootContext performBlock:^{
for (NSManagedObject *mo in rootContext.registeredObjects)
[rootContext refreshObject:mo mergeChanges:NO];
}];
}];
顶部的不会释放对象,底部的会。
最佳答案
我查看了您的示例项目。感谢发帖。
首先,您看到的行为不是错误……至少在 Core Data 中不是。如您所知,关系会导致保留周期,必须手动中断(此处记录:https://developer.apple.com/library/mac/#documentation/cocoa/Conceptual/CoreData/Articles/cdMemory.html)。
您的代码在 didSave:
中执行此操作.可能有更好的地方可以打破循环,但那是另一回事。
请注意,您可以通过查看 registeredObjects
轻松查看在 MOC 中注册的对象。属性(property)。
但是,您的示例将永远不会释放根上下文中的引用,因为 processPendingEvents
从来没有在那个 MOC 上调用过。因此,MOC 中的注册对象永远不会被释放。
Core Data 有一个称为“用户事件”的概念。默认情况下,“用户事件”被正确包装在主运行循环中。
但是,对于不在主线程上的 MOC,您有责任确保正确处理用户事件。请参阅此文档:http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/CoreData/Articles/cdConcurrency.html ,特别是标题为 Track Changes in Other Threads Using Notifications
的部分的最后一段.
当您调用 performBlock
你给它的 block 被包装在一个完整的用户事件中。然而,performBlockAndWait
的情况并非如此。 .因此,私有(private)上下文 MOC 会将这些对象保存在其 registeredObjects
中。收集到 processPendingChanges
叫做。
在您的示例中,如果您调用 processPendingChanges
,您可以看到释放的对象。里面performBlockAndWait
或将其更改为 performBlock
.其中任何一个都将确保 MOC 完成当前用户事件并从 registeredObjects
中删除对象。收藏。
编辑
响应您的编辑...并不是第一个不解除分配对象。就是 MOC 仍然将对象注册为故障。那发生在保存之后,在同一事件中。如果您只是发出一个无操作 block [context performBlock:^{}]
您将看到从 MOC 中删除的对象。
因此,您无需担心,因为在该 MOC 的下一次操作中,对象将被清除。你不应该有一个长期运行的背景 MOC 无论如何什么都不做,所以这对你来说真的没什么大不了的。
通常,您不想只刷新所有对象。但是,如果您想在保存后删除所有对象,那么您在 didSave:
中执行此操作的原始概念是合理的,因为这发生在保存过程中。但是,这将在所有上下文中出错(您可能不想要)。对于后台 MOC,您可能只需要这种严厉的方法。您可以查看 object.managedObjectContext
在 didSave:
但这不是一个好主意。最好为 DidSave 通知安装一个处理程序......
id observer = [[NSNotificationCenter defaultCenter]
addObserverForName:NSManagedObjectContextDidSaveNotification
object:rootContext
queue:nil
usingBlock:^(NSNotification *note) {
for (NSManagedObject *mo in rootContext.registeredObjects) {
[rootContext refreshObject:mo mergeChanges:NO];
}
}];
你会看到这可能会给你你想要的……虽然只有你可以确定你真正想要完成的事情。
关于ios5 - 核心数据 - 打破父上下文的保留周期,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15999932/