同时在后台同步时在UI交互上操作Core Data Context的最佳实践

时间:2017-10-31 04:12:40

标签: ios swift core-data

我已经在很多关于如何使用NSManagedObjectContext的文章和讨论中做过研究,但仍然无法为我的项目找到令人满意的架构。

在我的应用程序中,有三个来源可以修改数据,在同时发生冲突时按优先顺序排序(例如,云的优先级最低):

  1. 用户界面,
  2. BLE消息,
  3. 来自云的HTTP响应
  4. 由于我还不是iOS开发方面的专家,我试图避免为每个源使用多个上下文。然而,经过数周的反复试验,我不情愿但是开始考虑是否真的需要采用多上下文方法。

    一开始,我尝试在主要上下文中使用context.perform { }来执行所有数据更改操作(添加/更新/删除,但 fetch )。我保持fetch是一个同步功能,因为我希望数据提取是即时的,以便能够响应UI。但是,根据这种方法,我偶尔会收到"Collection <__NSCFSet: 0x000000000> was mutated while being enumerated"异常(我认为可能会在forEachmap函数中进行批处理数据处理。我还发现,当有大量记录要在后台队列中更新时,这种方法仍会阻止UI。

    因此,我创建了一个背景上下文并使用父子模型来操作数据。基本上,主要上下文(父)仅负责获取数据,而后台上下文(子)通过backgroundContext.perform { }操纵所有数据更改(添加/更新/删除)。这种方法解决了UI块问题,但是集合变异错误偶尔也会发生,并且在这种结构下会出现另一个问题:例如,当我在ViewController A中添加数据记录时,应用程序会崩溃,并移动到View Controller B,即使后台上下文尚未完成添加数据记录,也会立即获取相同的数据。

    因此,我想就我的项目中的核心数据用法提出一些建议。在我的父子数据上下文模型下,我做错了吗?或者,如果没有亲子,我是否应该不可避免地选择真正的多上下文模型?怎么做?

    我的主要上下文(父级)和后台上下文(子级)的启动方式如下:

    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 个答案:

答案 0 :(得分:0)

您是否曾探索过使用NSFetchedResultsController来获取数据? NSFetchedResultsController提供了一种线程安全的方法来观察由于CoreData中的创建,更新或删除操作而导致的数据更改。 使用NSFetchedResultsController的委托方法更新UI。 理想情况下,controllerDidChangeContent委托会给您一个更新UI的指示。 Documentation for reference