ios5 - 核心数据 - 打破父上下文的保留周期

标签 ios5 core-data fault retain-cycle

假设我们在核心数据模型中有两个实体:部门和员工。
部门与员工是一对多的关系。

我有以下 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.managedObjectContextdidSave:但这不是一个好主意。最好为 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/

相关文章:

compilation - iOS 编译几个库(armv7、i386)。配置脚本 : C preprocessor fails sanity check

objective-c - 选项卡栏 Controller 未正确显示

objective-c - 奇怪的 BAD_ACCESS 错误

ios - NSFetchRequest 找不到实体名称的 NSEntityDescription

ios - 强制iOS应用程序的数据库升级

c++ - 0xfeeefeee 段错误,但简单的分配更改?

ios - 具有模态转场的 UISplitViewController 不旋转

ios - 魔术记录,从UITableView中删除“第0节中的行数无效……”

c - 为什么在下面的代码上出现段错误?

c堆内存布局