核心数据管理对象上下文设计建议

时间:2012-11-02 23:49:57

标签: ios multithreading core-data nsmanagedobjectcontext

我们正在开发一个企业级应用程序,它将使用Core Data存储数万个对象,我们在几个方面遇到了问题。


我们的应用程序有几个独立的系统,可在需要时对数据进行操作。这些系统包括项目的发现,项目的加载,同步和UI显示。如果我们正确地设计我们的软件,由于不同的系统修改相同的对象,应该几乎没有合并冲突。每个系统都有自己的操作队列,所有操作队列都在后台执行。我们希望在后台保留所有对象的创建和修改,以最大限度地减少UI性能问题,尤其是在初始加速期间,可能会从服务器上的数据创建数千个对象。在这里,我们遇到了各种设计尝试的几个问题。在这些升级期间消耗大量内存,以及所有上下文和子上下文的错误编排,导致死锁和崩溃。 我们尝试了以下设计:

  • 一个根NSPrivateQueueConcurrencyType托管对象上下文,其中包含一个子NSMainQueueConcurrencyType上下文。 UI获取的结果控制器使用此子上下文从中获取结果。从NSMainQueueConcurrencyType子上下文中,我们创建了一个NSPrivateQueueConcurrencyType子上下文,我们将其称为“savingContext”,每个后台操作创建了一个“savingContext”的子上下文,进行了更改,最后完成了我们的操作称为“深度保存”,递归保存到顶部。我们最初选择此设计时不必处理来自许多不同子环境的NSManagedObjectContextDidSaveNotification通知。我们将每次调用都包含在NSPrivateQueueConcurrencyType上下文中,并使用performBlockAndWait:访问对象。功能上,这个设计执行。所有更改和插入都保存到持久性存储中,并且UI已根据更改进行更新。这,介绍了两个问题。由于合并后的更改通过NSMainQueueConcurrencyType子上下文,因此在升级期间会出现滞后的UI,更重要的是,在升级期间内存使用率非常高。由于无法在上下文中递归调用reset(因为主UI子上下文也在那里)和/或缺乏何时调用refreshObject:mergeChanges:的知识,我们会遇到过高的RAM使用率。所以我们走了一条不同的道路。
  • 有两个与持久性商店协调员链接的顶级上下文,一个NSPrivateQueueConcurrencyType用于保存子上下文,一个NSMainQueueConcurrencyType用于UI显示。 NSMainQueueConcurrencyType侦听来自主NSManagedObjectContextDidSaveNotification上下文的NSPrivateQueueConcurrencyType通知,并将它们合并到主线程中。每个后台操作都创建主NSPrivateQueueConcurrencyType上下文的子上下文,也使用私有队列并发类型,执行它的操作,递归执行“深度保存”,对当前上下文执行保存,递归调用深度保存到其父级,调用当前上下文的重置并再次保存。这样我们就可以避免内存问题,因为创建的对象在保存后会很快释放。但是,使用此设计,尽管存在NSInternalInconsistencyException上下文的保存通知,但我们遇到了大量问题,例如死锁,NSMainQueueConcurrencyType异常和获取结果控制器未更新UI。这也会导致UI中的初始加载时间减慢很多。在之前的设计中,获取的结果控制器返回的结果非常快,而这会阻止UI几秒钟,直到视图加载(我们在viewDidLoad中初始化获取的结果控制器)。

我们已经尝试了很多中间设计,但它们都围绕着相同的问题,要么是内存使用率非常高,请求结果控制器没有更新UI,也没有死锁和NSInternalInconsistencyException例外。


我真的很沮丧。我不禁觉得我们的设计过于复杂,应该是相当简单的事情,只是我们缺乏理解一些基本的东西会杀死我们。


那么你们会建议什么?你会为我们的背景推荐什么安排?我们应该如何在不同的线程中管理不同的上下文?释放插入对象和重置上下文的最佳实践?避免死锁?到目前为止,所有的帮助都将受到赞赏。


我也看过MagicalRecords类别的推荐。推荐吗?我们已经投入使用核心数据类型,使用MR进行迁移有多难?

1 个答案:

答案 0 :(得分:6)

首先,为了管理你的记忆,你的第二个架构为你提供了更大的灵活性。

其次,要管理两种内存:malloc-ed内存和驻留VM内存。您可以拥有较低的malloc内存占用空间,并且仍然具有较大的VM驻留区域。根据我的经验,这是因为Core Data积极地坚持新插入的项目。我通过保存后的修剪通知解决了这个问题。

第三,MOC很便宜。使用'和扔掉。换句话说,早期和经常释放记忆。

第四,尝试在主要MOC上几乎不做任何基于数据的数据。是的,这听起来适得其反。我的意思是所有复杂的查询都应该在后台线程上完成,然后将结果传递给主线程,或者在利用现在填充的行缓存时从主线程重新执行查询。通过这样做,您可以保持用户界面的直播。

第五,在我的多排队应用程序中,我尝试让所有的保存工作真正发生在后台。这使我的主要MOC保持快速且与来自网络的数据保持一致。

第六,NSFetchedResultsController是一个非常有用但专业的控制器。如果您的应用程序将其推送到其能力范围之外,它将开始锁定您的界面。当发生这种情况时,我会通过自己监听-didSave通知来滚动我自己的控制器。