核心数据 - 中断父上下​​文的保留周期

Zyp*_*rax 14 core-data fault ios5 retain-cycle

假设我们在Core Data模型中有两个实体:Departments和Employees.
该部门与员工有一对多的关系.

我有以下ManagedObjectContexts:
- Root:连接到持久性存储协调器
- Main:具有父Root的上下文

当我想创建一个Employee我做到以下几点:
-我有一个部门的主要方面
-我在主环境中创建一个Employee
-我分配部门到员工的部门属性
-我省主要方面
-我保存根上下文

这将在Main上下文和Root上下文中创建一个保留周期.

如果我没有子上下文(所有在Root上下文中)这样做,那么我可以通过调用refreshObject:mergeChangesEmployee 来打破保留周期.在我使用这两个上下文的情况下,我仍然可以使用该方法来打破Main上下文的循环,但是我如何打破Root上下文的循环呢?

旁注:这是一个描述我的问题的简单示例.在仪器中,我可以清楚地看到分配数量的增长.在我的应用程序中,我的上下文比一个级别更深,导致更大的问题,因为我得到了一个新的实体分配,每个上下文保留周期我正在保存.

更新15/04:NSPrivateQueueConcurrencyType vs NSMainQueueConcurrencyType
保存两个上下文后,我可以refreshObject:mergeChanges使用Department对象在Main上下文中执行.正如预期的那样,这将使Department对象重新出错,打破保留周期并在该上下文中取消分配Department和Employee实体.

下一步是打破Root上下文中存在的保留周期(保存Main上下文已将实体传播到Root上下文).我可以在这里做同样的技巧,并refreshObject:mergeChanges在Department对象的Root上下文中使用.

奇怪的是:当NSMainQueueConcurrencyType创建我的根上下文(所有分配重新故障和dealloced)这工作得很好,但与NSPrivateQueueConcurrencyType创建我的根上下文(所有分配的重新出现故障,但不工作 dealloced ).

附注:Root上下文的所有操作都在performBlock(AndWait)调用中完成

更新15/04:第2部分
当我做另外一个(没用,因为没有改变)保存或回滚与NSPrivateQueueConcurrencyType根上下文,对象似乎被释放.我不明白为什么这与NSMainQueueConcurrencyType的行为不一样.

更新16/04:演示项目
我创建了一个演示项目:http://codegazer.com/code/CoreDataTest.zip

更新21/04:到达那里
感谢Jody Hagings的帮助!
我试图refreshObject:mergeChanges移出我的ManagedObject didSave方法.

你能解释一下我之间的区别吗:

[rootContext performBlock:^{
    [rootContext save:nil];
    for (NSManagedObject *mo in rootContext.registeredObjects)
        [rootContext refreshObject:mo mergeChanges:NO];
}];
Run Code Online (Sandbox Code Playgroud)

[rootContext performBlock:^{
    [rootContext save:nil];
    [rootContext performBlock:^{
        for (NSManagedObject *mo in rootContext.registeredObjects)
            [rootContext refreshObject:mo mergeChanges:NO];
    }];
}];
Run Code Online (Sandbox Code Playgroud)

顶部的一个不会释放对象,底部的就是释放.

Jod*_*ins 10

我查看了你的示例项目.感谢张贴.

首先,您看到的行为不是错误......至少在核心数据中没有.如您所知,关系会导致保留周期,必须手动打破(请在此处记录:https://developer.apple.com/library/mac/#documentation/cocoa/Conceptual/CoreData/Articles/cdMemory.html).

你的代码是这样做的didSave:.可能有更好的地方打破这个循环,但这是另一回事.

请注意,您可以通过查看registeredObjects属性轻松查看在MOC中注册的对象.

但是,您的示例永远不会在根上下文中释放引用,因为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块时,你将它包含在一个完整的用户事件中.但事实并非如此performBlockAndWait.因此,私有上下文MOC将这些对象保留在其registeredObjects集合中,直到processPendingChanges被调用.

在您的示例中,如果您在processPendingChanges内部调用performBlockAndWait或将其更改为,则可以看到释放的对象performBlock.这些中的任何一个都将确保MOC完成当前用户事件并从registeredObjects集合中删除对象.

编辑

响应你的编辑......并不是第一个没有释放对象.这就是MOC仍然将对象注册为故障.在同一事件中,在保存之后发生了这种情况.如果您只是发出no-op块,[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];
    }
}];
Run Code Online (Sandbox Code Playgroud)

你会看到这可能会给你你想要的东西......虽然只有你可以确定你真正想要完成的事情.