假设在核心数据模型中有两个实体:部门和员工。
该部门与员工之间存在一对多关系。

我有以下ManagedObjectContexts:
-根:连接到持久性存储协调器
-主要:父级根的上下文

当我要创建员工时,请执行以下操作:
-我的主要部门有部门
-我在Main上下文中创建了一个Employee
-我将部门分配给员工的部门财产
-我保存主上下文
-我保存了根上下文

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

如果我在没有子上下文(都在Root上下文中)的情况下执行此操作,则可以通过在Employee上调用refreshObject:mergeChanges来打破保留周期。在两种情况下,我仍然可以使用该方法来中断Main上下文上的周期,但是我将如何打破Root上下文上的周期?

旁注:这是描述我的问题的简单示例。在“工具”中,我可以清楚地看到分配数量的增长。在我的应用程序中,我所处的上下文要比一个层次更深,这会导致更大的问题,因为我得到了一个新的实体分配,并且要保存每个上下文的保留周期。

更新15/04:NSPrivateQueueConcurrencyType与NSMainQueueConcurrencyType
保存两个上下文之后,我可以使用Department对象在Main上下文上执行refreshObject:mergeChanges。正如预期的那样,这将重新破坏Department对象,打破保留周期,并在这种情况下重新分配Department和Employee实体。

下一步是打破根上下文中存在的保留周期(保存Main上下文已将实体传播到Root上下文)。我可以在此处执行相同的技巧,并在带有Department对象的Root上下文中使用refreshObject:mergeChanges

奇怪的是:当使用NSMainQueueConcurrencyType创建我的Root上下文时(所有分配都重新故障并取消分配),此方法可以正常工作,但是当使用NSPrivateQueueConcurrencyType创建我的Root上下文时(所有分配都被重新故障但未取消分配)时,此方法不起作用)。

旁注:针对Root上下文的所有操作均在performBlock(AndWait)调用中完成

更新15/04:第2部分
当我使用NSPrivateQueueConcurrencyType对Root上下文进行另一次保存(无用,因为没有更改)时,对象似乎已被释放。我不明白为什么这与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];
}];




[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中注册了哪些对象。

但是,您的示例将永远不会在根上下文中释放引用,因为从未在该MOC上调用processPendingEvents。因此,MOC中已注册的对象将永远不会被释放。

核心数据的概念称为“用户事件”。默认情况下,“用户事件”正确包装在主运行循环中。

但是,对于不在主线程上的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仍然将对象注册为故障。在保存之后,在同一事件中发生了这种情况。如果仅发出无操作块[context performBlock:^{}],您将看到从MOC中删除的对象。

因此,您不必担心它,因为在该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/

10-10 17:17