9

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


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

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

我们尝试了许多中间设计,但它们都围绕着相同的问题,要么是非常高的内存使用,要么是获取结果控制器不更新 UI,要么是死锁和NSInternalInconsistencyException异常。


我真的越来越沮丧。我不能不觉得我们的设计对于一些应该相当简单的东西来说过于复杂了,只是我们缺乏对某些基本原理的理解正在扼杀我们。


那么大家有什么建议呢?对于我们的情况,您会推荐什么安排?我们应该如何管理不同线程中的不同上下文?释放插入对象和重置上下文的最佳实践?避免死锁?在这一点上,所有帮助将不胜感激。


我还看到了有关 MagicalRecords 类别的建议。推荐吗?我们已经投入使用 Core Data 类型,使用 MR 迁移会有多困难?

4

1 回答 1

6

首先,为了管理您的内存,您的第二种架构为您提供了更大的灵活性。

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

第三,MOC 价格便宜。使用它们并扔掉它们。换句话说,尽早并经常释放内存。

第四,在主 MOC 上尽量不做任何基于数据库的操作。是的,这听起来适得其反。我的意思是,您所有的复杂查询确实应该在后台线程上完成,然后将结果传递给主线程,或者在利用现在填充的行缓存时从主线程重做查询。通过这样做,您可以保持 UI 处于活动状态。

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

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

于 2012-11-03T16:54:47.080 回答