在后台同步的同时在 UI 交互上操作 Core Data Context 的最佳实践
Posted
技术标签:
【中文标题】在后台同步的同时在 UI 交互上操作 Core Data Context 的最佳实践【英文标题】:Best practice manipulating Core Data Context on UI interaction while sync in background at the same time 【发布时间】:2017-10-31 04:12:40 【问题描述】:我已经研究了很多关于如何使用NSManagedObjectContext
的文章和讨论,但仍然无法为我的项目找到令人满意的架构。
在我的应用程序中,可以从三个来源修改数据,当同时发生冲突时按优先级排序(例如,云的优先级最低):
-
用户界面,
BLE 消息,
来自云端的 HTTP 响应
由于我仍然不是 ios 开发方面的专家,因此我尽量避免为每个源使用多个上下文。然而,经过数周的反复试验,我很不情愿,但开始考虑是否真的需要采用多上下文方法。
一开始,我尝试在主上下文上使用context.perform
来做所有的数据更改操作(add/update/delete,除了fetch) .我将 fetch 保持为同步功能,因为我希望数据获取是即时的,以便可以响应 UI。但是,在这种方法下,我偶尔会收到"Collection <__NSCFSet: 0x000000000> was mutated while being enumerated"
异常(我想这可能发生在用于批处理数据处理的forEach
或map
函数中)。我还发现,当后台队列中有大量记录要更新时,这种方法仍然会阻塞 UI。
因此,我创建了一个后台上下文并使用父子模型来操作数据。基本上主上下文(父)只负责获取数据,而后台上下文(子)通过backgroundContext.perform
操作所有数据更改(添加/更新/删除)。这种方法解决了 UI 阻塞问题,但是偶尔会出现 collection mutated 错误,并且在这种结构下还会出现另一个问题:例如,当我在 ViewController A 中添加一条数据记录并移动到 View Controller B 时,应用程序会崩溃,这即使后台上下文尚未完成添加数据记录,也会立即获取相同的数据。
因此,我想就我的项目中使用 Core Data 提出一些建议。在我的父子数据上下文模型下我做错了什么吗?或者,我应该不可避免地选择一个没有父子的真正的多上下文模型吗?怎么做?
我的主要上下文(父)和背景上下文(子)是这样启动的:
lazy var _context: NSManagedObjectContext =
return (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
()
lazy var _backgroundContext: NSManagedObjectContext =
let ctx = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.privateQueueConcurrencyType)
ctx.parent = self._context
return ctx
()
合并函数如下:
@objc func contextDidSaveContext(notification: NSNotification)
let sender = notification.object as! NSManagedObjectContext
if sender === self._context
NSLog("******** Saved main Context in this thread")
self._backgroundContext.perform
self._backgroundContext.mergeChanges(fromContextDidSave: notification as Notification)
else if sender === self._backgroundContext
NSLog("******** Saved background Context in this thread")
self._context.perform
self._context.mergeChanges(fromContextDidSave: notification as Notification)
else
NSLog("******** Saved Context in other thread")
self._context.perform
self._context.mergeChanges(fromContextDidSave: notification as Notification)
self._backgroundContext.perform
self._backgroundContext.mergeChanges(fromContextDidSave: notification as Notification)
任何关于核心数据结构的建议都将不胜感激。
【问题讨论】:
【参考方案1】:您是否探索过使用 NSFetchedResultsController 来获取数据?
NSFetchedResultsController 提供了一种线程安全的方式来观察由于 CoreData 中的创建、更新或删除操作而导致的数据变化。
使用 NSFetchedResultsController 的委托方法来更新 UI。
理想情况下,controllerDidChangeContent
delegate 会给你一个更新 UI 的指示。 Documentation for reference
【讨论】:
以上是关于在后台同步的同时在 UI 交互上操作 Core Data Context 的最佳实践的主要内容,如果未能解决你的问题,请参考以下文章