我们正在开发一个企业级应用程序,它将使用核心数据存储数以万计的对象,但我们在多个方面都遇到了问题。
我们的应用程序有几个独立的系统,可以在需要时对数据进行操作。这些系统包括项目的发现、项目的加载、同步和UI显示。如果我们正确地设计我们的软件,那么由于不同的系统修改相同的对象而导致的合并冲突应该很少甚至没有。每个系统都有自己的操作队列,全部在后台执行。我们希望将所有对象创建和修改保留在后台,以最大程度地减少 UI 性能问题,尤其是在初始启动期间,可能会根据服务器上的数据创建数千个对象。在这里,我们的各种设计尝试遇到了一些问题。在这些启动过程中会消耗大量内存,并且所有上下文和子上下文的编排不正确,从而导致死锁和崩溃。
我们尝试了以下设计:
- 一根
NSPrivateQueueConcurrencyType
有一个子对象的托管对象上下文NSMainQueueConcurrencyType
语境。 UI 获取结果控制器使用此子上下文来获取结果。来自NSMainQueueConcurrencyType
子上下文,我们创建了一个NSPrivateQueueConcurrencyType
子上下文,我们称之为“ savingContext”,每个后台操作都会创建该“ savingContext”的子上下文,进行更改,最后执行我们所谓的“深度保存”,递归地保存到顶部。我们最初选择这种设计是为了不必处理NSManagedObjectContextDidSaveNotification
来自许多不同子上下文的通知。我们结束了对NSPrivateQueueConcurrencyType
上下文和对对象的访问performBlockAndWait:
。从功能上来说,这个设计是有效的。所有更改和插入都保存到持久存储中,并且 UI 随更改而更新。这引入了两个问题。其中之一是在启动过程中用户界面滞后,因为合并的更改经过NSMainQueueConcurrencyType
子上下文,更重要的是,在启动期间内存使用率非常高。由于无法调用,我们会遇到过高的 RAM 使用量reset
在上下文中递归(因为主 UI 子上下文也在那里)和/或缺乏何时调用的知识refreshObject:mergeChanges:
。于是我们走了一条不同的路。
- 有两个与持久存储协调器链接的顶级上下文,一个
NSPrivateQueueConcurrencyType
用于保存子上下文,以及NSMainQueueConcurrencyType
用于UI显示。这NSMainQueueConcurrencyType
听NSManagedObjectContextDidSaveNotification
来自主站的通知NSPrivateQueueConcurrencyType
context 并将它们合并到主线程中。每个后台操作都会创建一个主上下文的子上下文NSPrivateQueueConcurrencyType
context,也具有私有队列并发类型,执行它的操作,递归地执行“深度保存”,即对当前上下文执行保存,对其父级执行深度保存的递归调用,对当前上下文调用重置并再次保存。这样我们就可以避免内存问题,因为创建的对象在保存后会快速释放。然而,通过这种设计,我们遇到了很多问题,例如死锁、NSInternalInconsistencyException
异常和获取的结果控制器不更新 UI,尽管有保存通知NSMainQueueConcurrencyType
语境。这也会导致 UI 中的初始加载时间显着减慢。在之前的设计中,获取结果控制器返回结果的速度非常快,而这会导致 UI 阻塞几秒钟,直到视图加载(我们在中初始化获取结果控制器)viewDidLoad
).
我们尝试了许多中间设计,但它们都围绕着相同的问题,要么非常高的内存使用率,获取的结果控制器不更新 UI 或死锁,NSInternalInconsistencyException
例外情况。
我真的很沮丧。我忍不住觉得我们的设计对于一些本应相当简单的东西来说过于复杂,而正是我们缺乏对一些基本原理的理解才导致了我们的死亡。
那么你们有什么建议呢?对于我们的环境,您会推荐什么安排?我们应该如何管理不同线程中的不同上下文?释放插入对象和重置上下文的最佳实践?避免死锁?此时,我们将不胜感激所有帮助。
我还看到了 MagicalRecords 类别的推荐。推荐吗?我们已经投资使用核心数据类型,那么使用 MR 进行迁移有多困难?
首先,为了管理内存,第二种架构为您提供了更大的灵活性。
其次,有两种内存需要管理:malloc 内存和常驻 VM 内存。您可以拥有较低的 malloc 内存占用量,但仍然拥有较大的 VM 驻留区域。根据我的经验,这是由于 Core Data 积极保留新插入的项目。我通过保存后修剪通知解决了这个问题。
第三,MOC 价格便宜。使用它们并扔掉它们。换句话说,尽早且经常释放内存。
第四,尝试在主 MOC 上几乎不做任何数据库方面的事情。是的,这听起来适得其反。我的意思是,所有复杂的查询实际上都应该在后台线程上完成,然后将结果传递到主线程,或者在利用现在填充的行缓存的同时从主线程重做查询。通过这样做,您可以保持 UI 的活力。
第五,在我的多队列应用程序中,我尝试让所有保存真正发生在后台。这使我的主要 MOC 保持快速并与来自网络的数据保持一致。
第六,NSFetchedResultsController 是一个非常有用但专门的控制器。如果您的应用程序将其推到其权限范围之外,它就会开始锁定您的界面。当发生这种情况时,我会通过自己监听 -didSave 通知来滚动自己的控制器。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)