有问题的NSManagedObject随时间积累

时间:2014-01-27 03:34:01

标签: ios core-data

我有一个应用程序,它不断从TCP / IP端点接收XML消息流。在收到每条消息后,应用程序将其内容摘要到一组核心数据实体中。这是通过三个上下文结构完成的:

  • 主人(私人队列)
  • Main(主要队列 - > Master)
  • Stream(专用队列 - > Main)

这种安排使流处理脱离主线程。该应用程序通常每秒或每两秒收到10到150条消息。在解构和保留每条消息之后,保存Stream上下文。 A6级设备的CPU使用率通常不到15%。

然而我的问题是记忆。如果我将NSFetchedResultsController连接到Main上下文,我会在消息到达时得到一个很好的消息流。但是,如果我配置文件,我会注意到我的NSManagedObject计数逐渐增加。最终内存压力将导致应用程序终止。

经过12分钟的分析后,该应用程序已消耗了6300条XML消息并解析了121,000个属性。这些属性消耗7.8MB,消息消耗438KB,现在应用程序总大小为54MB。显然这是不可持续的。

乐器指出所有物体仍然存在。在互联网周围徘徊让我相信我可能有一个保留周期导致物体不会出现故障。但是,使用“refreshObject”的建议在此处适用的文档中并不清楚。

收到XML后,将创建一个Message实体。接下来,使用XML的根节点作为其名称和相关位来创建Type实体。类似地,对于这些元素的每个元素和子元素以及XML的任何内联属性,都会创建一个属性元素。这是有趣的部分,因为它具有对消息的引用(对于所有属性的平面表示)以及与其自身的分层的子属性关系。在此过程结束时,将保存上下文,Main上下文将其选中,FRC将显示新行。

一种想法是在每隔几百条消息保存后重置Stream上下文。如果我断开FRC,我可以保持基本水平 - 但是当我连接FRC时,这感觉不对,并没有解决问题。

任何想法都会受到赞赏。

1 个答案:

答案 0 :(得分:5)

我建议使用用于Master上下文的相同持久性存储协调器配置Stream上下文。并且可能会定期重置流上下文。

在当前配置中,Stream上下文填充会给其父级带来额外压力。如果Stream上下文中发生了大量更新,则此压力变得更加明显。

首先,当Stream上下文需要执行需要锁定的操作时,它将锁定父项。

其次,当在Stream上下文中进行保存时,所有更改都会被推回到父上下文。你无法控制它。如果在Main上下文中有一个获取的结果控制器,那么在保存时它将逐个重放所有更改。如果更新很大,它将带来很大的开销。绝对在CPU中,可能在内存中。

我认为在后台处理大型更新并刷新UI(特别是使用获取的结果控制器)的最佳模式是配置直接使用持久性存储协调器执行大更新的上下文。然后,当发生大的更新时,只需在UI上下文中重新获取。并且不要忘记将获取请求上的获取批量大小设置为对您的案例值有意义的一些。您可以从屏幕上可见的单元格数开始。

这种模式效率更高,但复杂性成本更高。您需要考虑如何在其他上下文中刷新数据。您需要注意这一点,因为Core Data不会触及完全实现的对象。 (由于Apple向我确认了错误,因此设置setShouldRefreshRefetchedObjects也无济于事。)

例如,您在Main上下文中获取了一些对象,访问了其属性以在屏幕上显示它。这个对象不再是一个错误。然后,您的Stream上下文(现在直接使用持久性存储协调器配置)更新了相同的属性。即使您在Main上下文中重新获取并且该对象将在搜索结果中,也不会更新对象属性。

所以你可以使用这样的东西:

- (void)refreshObjectsOnContextDidSaveNotification:(NSNotification *)notification {
    NSSet *updatedObjects = notification.userInfo[NSUpdatedObjectsKey];
    NSSet *updatedObjectIDs = [updatedObjects valueForKey:@"objectID"];

    [self.mainContext performBlock:^{
        for (NSManagedObject *object in [self.mainContext registeredObjects]) {
            if (![object isFault] && [updatedObjectIDs containsObject:[object objectID]]) {
                [self.mainContext refreshObject:object mergeChanges:YES];
            }
        }
    }];

    [self.masterContext performBlock:^{
        for (NSManagedObject *object in [self.masterContext registeredObjects]) {
            if (![object isFault] && [updatedObjectIDs containsObject:[object objectID]]) {
                [self.masterContext refreshObject:object mergeChanges:YES];
            }
        }
    }];
}

这将刷新主要和主要上下文中的更新对象。

当Stream上下文中的保存不是很大时,您可以使用标准合并方法将更改合并到其他两个上下文中。使用获取结果控制器时,您将能够在对象删除和插入时看到漂亮的单元格动画。保存中受影响的对象数量可以通过上下文保存通知中的用户信息的NSInsertedObjectsKeyNSUpdatedObjectsKeyNSDeletedObjectsKey键获得。

在每次重大保存后,您都可以重置Stream上下文。只是不要忘记在重置后您无法在此上下文中访问任何以前获取的对象。