【发布时间】:2012-10-23 15:56:52
【问题描述】:
我们正在开发一个企业级应用程序,该应用程序将使用 Core Data 存储数以万计的对象,我们在几个方面遇到了问题。
我们的应用程序有几个独立的系统,它们在需要时对数据进行操作。这些系统包括项目的发现、项目的加载、同步和 UI 显示。如果我们正确地设计我们的软件,由于不同的系统修改相同的对象,应该几乎没有合并冲突。每个系统都有自己的操作队列,全部在后台执行。我们希望将所有对象的创建和修改都保留在后台,以最大限度地减少 UI 性能问题,尤其是在初始加速期间,可能会从服务器上的数据创建数千个对象。在这里,我们在各种设计尝试中遇到了几个问题。在这些加速过程中会消耗大量内存,并且所有上下文和子上下文的编排不正确,从而导致死锁和崩溃。 我们尝试了以下设计:
- 一个根
NSPrivateQueueConcurrencyType托管对象上下文,它有一个子NSMainQueueConcurrencyType上下文。 UI 获取结果控制器使用此子上下文从中获取结果。从NSMainQueueConcurrencyType子上下文中,我们创建了一个NSPrivateQueueConcurrencyType子上下文,我们称之为“savingContext”,每个后台操作都创建了该“savingContext”的一个子上下文,进行了更改,最后完成了我们所说的“深度保存”,递归保存到顶部。我们最初选择这种设计是为了不必处理来自许多不同子上下文的NSManagedObjectContextDidSaveNotification通知。我们包装了对NSPrivateQueueConcurrencyType上下文的每次调用,并使用performBlockAndWait:来访问对象。在功能上,这个设计执行了。所有更改和插入都保存到持久存储中,并且 UI 随更改而更新。至此,引入了两个问题。一个是在加速期间 UI 滞后,因为合并更改通过NSMainQueueConcurrencyType子上下文,更重要的是,在加速期间内存使用率非常高。由于无法在上下文中递归调用reset(因为主 UI 子上下文也在那里)和/或不知道何时调用refreshObject:mergeChanges:,我们会遇到令人望而却步的 RAM 使用。所以我们走了一条不同的路。 - 有两个与持久存储协调器链接的顶级上下文,一个
NSPrivateQueueConcurrencyType用于保存子上下文,一个NSMainQueueConcurrencyType用于UI 显示。NSMainQueueConcurrencyType监听来自主NSPrivateQueueConcurrencyType上下文的NSManagedObjectContextDidSaveNotification通知并将它们合并到主线程中。每个后台操作都会创建一个主NSPrivateQueueConcurrencyType上下文的子上下文,也具有私有队列并发类型,执行它的工作,递归执行“深度保存”,对当前上下文执行保存,递归调用深度保存对其父级,在当前上下文上调用 reset 并再次保存。这样我们就避免了内存问题,因为创建的对象在保存后会很快释放。但是,通过这种设计,我们遇到了很多问题,例如死锁、NSInternalInconsistencyException异常和获取的结果控制器不更新 UI,尽管有NSMainQueueConcurrencyType上下文的保存通知。这也会导致 UI 中的初始加载时间变慢很多。在之前的设计中,获取结果控制器返回结果的速度非常快,而这会使 UI 阻塞几秒钟,直到视图加载(我们在viewDidLoad中初始化获取结果控制器)。
我们尝试了许多中间设计,但它们都围绕着相同的问题,要么非常高的内存使用率,要么获取结果控制器不更新 UI,要么死锁和NSInternalInconsistencyException 异常。
我真的很沮丧。我不能不觉得我们的设计对于一些应该相当简单的东西来说过于复杂了,只是我们缺乏对某些基本原理的理解正在扼杀我们。
那你们有什么建议呢?对于我们的情况,您会推荐什么安排?我们应该如何管理不同线程中的不同上下文?释放插入对象和重置上下文的最佳实践?避免死锁?在这一点上,所有帮助将不胜感激。
我还看到了有关 MagicalRecords 类别的建议。推荐吗?我们已经投入使用 Core Data 类型,使用 MR 迁移会有多困难?
【问题讨论】:
-
我遇到了类似的问题(架构一):stackoverflow.com/questions/15999932/… - 你最终选择了什么样的架构?有什么建议吗?
-
我们有一个主上下文和一个父根上下文。但是根上下文仅用于保存,因此我们在每次保存时都将其重置。我们有无数的问题。似乎这些子上下文和父上下文的新功能没有经过深思熟虑,Apple 仍然没有处理许多错误。
-
我倾向于使用带有子 MOC (MainQueue) 的根 MOC (PrivateQueue) 的架构,并通过在我的一些 ManagedObjects 中的
-didSave中添加[self.managedObjectContext refreshObject:self mergeChanges:NO];调用来减少内存。这打破了关系之间的保留循环,并允许所有 MOC 解除分配对象。只有具有 NSPrivateQueueConcurrencyType 的 MOC 似乎有一个奇怪的问题,即它们不会立即释放对象,而是在下一次保存/回滚时释放。 -
这不会只释放已保存的对象(具有
hasChanges到YES)吗? (当didSave被调用时,我不是 100%。) -
NSManagedObjectContext 只保留有变化的 NSManagedObjects(将其视为动态强/弱)。保存后,将不再有任何更改,所有对象都将被释放,除非您自己保留它们。
标签: ios multithreading core-data nsmanagedobjectcontext