随时间的问题NSManagedObject累积 [英] Problematic NSManagedObject accumulation over time

查看:87
本文介绍了随时间的问题NSManagedObject累积的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have an application which continuously receives a stream of XML messages from a TCP/IP endpoint. Upon receipt of each message, the application digests it's contents into a set of core data entities. This is accomplished via a three context structure:


  • 主(私人队列)

  • Main queue - > Master)

  • Stream(Private queue - > Main)

流处理关闭主线程。该应用程序通常每秒或两次从任何地方接收10 - 150条消息。 Stream上下文的保存在每个消息被解构和持久化之后发生。在A6级别的设备上,CPU使用率通常不到15%。

This arrangement keeps the stream processing off the main thread. The application typically receives anywhere from 10 - 150 messages every second or two. Saving of the Stream context takes place after each message is deconstructed and persisted. CPU usage is typically short of 15% on an A6 level device.

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

My problem however is memory. If I hook up an NSFetchedResultsController to the Main context I get a nice flow of the messages as they arrive. However, if I profile I notice that my NSManagedObject count gradually increases. Eventually memory pressure will cause the app to terminate.

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

After 12 minutes of profiling, the app has consumed 6300 XML messages and parsed 121,000 properties. This consumes 7.8MB for the properties, 438KB for the messages and the total app size is now 54MB. Obviously this isn't sustainable.

仪器注意到所有的对象仍然活着。拖曳在网站周围让我相信我可能有一个保持周期,导致对象不是故障。但是,使用refreshObject的建议在文档中并不清楚,它将在这里应用。

Instruments notes that all of the objects are still live. Trolling around the interwebs leads me to believe I might have a retain cycle causing the objects to not be faulted. However, the suggestion of using "refreshObject" isn't clear in the documentation that it would apply here.

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

Once the XML has been received, a Message entity is created. Next a Type entity is created using the root node of the XML as it's name - and associated bits. Similarly for each element and sub element of those elements and any inline properties of the XML a property element is created. This is the fun part as it has a reference to the message (for a flat representation of all properties) as well as a hierarchal childProperties relationship to itself. At the end of this process the context is saved and the Main context picks it up and the FRC displays the new row.

一个想法是在保存每隔几百条消息。如果我断开FRC,我可以保持基本水平 - 但这感觉错误,并没有解决问题,当我连接FRC备份。

One thought was to reset the Stream context after a save every few hundred of messages persisted. If I disconnect the FRC, I can stay basically level - however this feels wrong and doesn't solve the problem when I wire the FRC back up.

任何想法将是赞赏。

推荐答案

我建议使用与主上下文相同的持久存储协调器来配置Stream上下文。也许可以定期重置流上下文。

I would suggest configuring your Stream context with the same persistent store coordinator that is used for the Master context. And maybe periodically reset the stream context.

在当前配置Stream上下文填充给其父级额外的压力。如果Stream上下文中发生大的更新,这个压力变得更加明显。

In the current configuration Stream context fill put additional pressure to its parents. And if big updates are happening in the Stream context, this pressure becomes more visible.

首先,当Stream上下文需要做一些需要锁定的事情时,

First, when the Stream context needs to do something that requires a lock, it will lock both parents.

其次,当在Stream上下文中进行保存时,所有更改都将推回到父上下文。你不能控制它。如果在Main上下文中有一个获取的结果控制器,那么在保存它将一个接一个地重放所有的变化。如果更新很大,会带来很大的开销。

Second, when save happens in the Stream context, all the changes are pushed back to the parent, the Main context. And you don’t have control over it. If there is a fetched results controller in the Main context, then on save it will replay all the changes one-by-one. And if the update is big, it will bring a big overhead. Definitely in CPU and probably in memory.

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

I think the best pattern for handling big updates in the background and refreshing the UI (especially with the fetched results controller) is to configure the context that does big updates directly with persistent store coordinator. And then, when big a update happens, just refetch in the UI context. And don’t forget to set fetch batch size on the fetch request to some meaningful to your case value. You could start with the number of cells visible on the screen.

这种模式更高效,但是带有复杂性成本。您需要考虑如何在其他上下文中刷新数据。你需要照顾这一点,因为Core Data不会触摸完全实现的对象。 (设置 setShouldRefreshRefetchedObjects 没有帮助,因为苹果向我证实的错误。)

This pattern is more efficient but comes with complexity cost. You need to think how to refresh the data in other contexts. You need to take care of this because Core Data doesn’t touch objects that are fully realized. (Setting setShouldRefreshRefetchedObjects doesn’t help either because of the bug confirmed to me by Apple.)

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

For example, you fetched some object in the Main context, accessed its property for displaying it on the screen. This object is not a fault any more. Then your Stream context (now configured with the persistent store coordinator directly) updated the same property. Even if you refetch in the Main context and the object will be in the search results, object properties will not be updated.

因此,您可以使用类似这样的: p>

So you could use something like this:

- (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];
            }
        }
    }];
}

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

This will refresh updated objects in main and master contexts.

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

When the save in the Stream context is not huge you could simply merge changes using the standard merge method into other two contexts. When fetched results controller is used, you’ll be able to see nice cell animations on object deletion and insertion. The number of objects affected in a save you can get from NSInsertedObjectsKey, NSUpdatedObjectsKey, and NSDeletedObjectsKey keys of the user info in the context-did-save notification.

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

And after each big save you could reset the Stream context. Just don’t forget that you can’t access any previously fetched objects in this context after the reset.

这篇关于随时间的问题NSManagedObject累积的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆