随着时间的推移有问题的NSManagedObject积累

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

  • 主人(私人队列)
  • 主(主队列 – >主)
  • stream(专用队列 – >主)

这种安排使stream处理不在主线程中。 应用程序通常每秒接收10到150条消息。 Stream消息的保存在每个消息被解构并保持之后进行。 在A6级别的设备上,CPU使用率通常短于15%。

我的问题是内存。 如果我将一个NSFetchedResultsController挂接到Main上下文,那么当消息到达时,我会得到一个很好的消息stream。 但是,如果我configuration文件我注意到我的NSManagedObject计数逐渐增加。 最终内存压力将导致应用程序终止。

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

仪器注意到所有的物体仍然存在。 在互联网上拖动,我相信我可能会有一个保留周期,导致对象不被错误。 但是,使用“refreshObject”的build议在文档中并不清楚,它在这里适用。

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

一个想法是在保存每隔几百个消息的情况下重置Stream上下文。 如果我断开FRC,我可以保持基本水平 – 但是这种感觉是错误的,并没有解决问题,当我连接FRC备份。

任何想法将不胜感激。

我会build议configuration您的stream上下文与用于主上下文的相同的持久存储协调器。 也许定期重置stream上下文。

在目前的configurationstream上下文填充给其父母施加额外的压力。 如果Stream上下文中发生大的更新,这个压力就会变得更加明显。

首先,当Stream上下文需要做一些需要locking的事情时,它会locking父母。

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

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

这种模式更有效率,但带有复杂性成本。 您需要考虑如何刷新其他上下文中的数据。 您需要注意这一点,因为核心数据不会触及完全实现的对象。 (设置setShouldRefreshRefetchedObjects并没有帮助,因为苹果证实了我的错误。)

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

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

 - (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上下文中的保存不是很大时,您可以简单地将使用标准合并方法的更改合并到其他两个上下文中。 当使用获取的结果控制器时,您将能够在对象删除和插入时看到漂亮的单元格animation。 保存中受影响的对象的数量可以从context-did-save通知中的用户信息的NSInsertedObjectsKeyNSUpdatedObjectsKeyNSDeletedObjectsKey键中获取。

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