【问题标题】:Can we generate Realm results in background queue and use it on main thread我们可以在后台队列中生成 Realm 结果并在主线程上使用它吗
【发布时间】:2018-01-07 03:33:55
【问题描述】:

我最近开始使用 Realm,我不确定我的用例是否有效:

通常,当从数据库读取大量数据时,我想将其放入后台队列中,以便异步获取数据并稍后在主线程上使用。

例如,我想根据城市获取几个结果:

    private var results: [Results<SomeObject>?] = []
    autoreleasepool {
        DispatchQueue(label: "background").async {
            [unowned self] in
            do
            {
                let realm = try Realm()
                for i in 1...City.count
                {
                    self.results.append(realm.objects(SomeObject.self).filter("city=\(i)"))
                }
            }
            catch
            {
                NSLog("Failed to open Realm instance on background qeueue")
            }
        }
    }

稍后使用results 更新我的图表:

cell.setChartData(ChartDataFactory.createCombinedData(from: results[0]))

但是,如果我将此模型应用于 Realm,则会出现类似

的错误

由于未捕获的异常“RLMException”而终止应用程序,原因:“从错误线程访问的领域。

我知道我必须为每个线程使用领域,我可以通过在主线程上读取领域来做到这一点,但我不希望领域查询阻塞我的主线程。

有什么方法可以实现我的目标吗?例如在后台队列中读取领域并从另一个线程访问结果,同时保持自动刷新功能。

谢谢。

【问题讨论】:

  • 嘿,你能弄清楚这个吗?似乎不可能将全新的领域对象从后台移动到主线程...我只看到从主线程转到后台的示例...

标签: ios swift realm


【解决方案1】:

Realm 具有在后台线程上运行查询并使用Results.observe() 将结果传递到主线程的内置功能。

如果您特别需要执行无法表示为 Realm 查询的昂贵过滤逻辑,您可以使用 ThreadSafeReference 在线程之间手动传递对象数组。

从 5.0 开始,您现在可以在后台线程上构建查询并在主线程上使用 on: 参数到 observe() 接收通知:

DispatchQueue.global().async {
    let realm = try! Realm()
    let results = realm.objects(ObjectType.self).filter("property in %@", expensiveFunction(realm))
    self.token = results.observe(on: .main) { change in
        // do stuff with the results on the main thread
    }
}

【讨论】:

    【解决方案2】:

    领域对象只能通过从中获取或创建它们的领域进行访问。领域实例不能在线程之间共享(您知道这一点),并且从特定领域实例共享 对象 到另一个线程,隐含地与在线程之间共享领域实例具有相同的效果。这是由于对象和领域实例之间的紧密耦合。

    正如这个 GitHub 问题 https://github.com/realm/realm-cocoa/issues/946 中所述,推荐的做法是共享主键(如果您的领域对象覆盖了 RealmObject (Objective-C) / Object (Swift) 的 primaryKey 方法)。

    【讨论】:

    • 但是共享主键仍然需要在目标线程上进行读取操作,对吧?这并不能解决后台读取和主线程使用情况。所以这是一个限制?
    • 当然,这需要在主线程上查找。但是,在 Realm 中通过主键查找非常快。您是否尝试过测量从 Realm 获取对象所需的时间?您可以编写一个获取大量对象的测试用例,并使用 XCTestCase 的 self.measure 功能来测量性能,以了解它会在主线程上停顿多少。但通常 Realm 实例和对象仅限于获取或创建它们的同一线程。
    【解决方案3】:

    您正在尝试从不同的队列直接访问“结果”属性,这会崩溃。您应该改用 Thomas 的回答中所示的 ThreadSafeReference。

    确保在从 Realm 数据库中获取结果之前为结果创建 ThreadSafeReference 并在后台队列上调用 realm.resolve()。

    【讨论】:

      【解决方案4】:

      我是这样解决的。我看到了整体性能改进,但我找不到任何在后台线程上查询的实现示例。可能有性能更好的解决方案。

      self.results = self.realm.objects(Object.self).filter(predicate).sorted(by: sortProperties)
      self.notificationToken = self.results.observe({ (notification) in
          self.tableview.reloadData()
      })
      

      这是 iPhone X 上的结果,它的数据库大约有 171k 项。持续时间以秒为单位。

      在 UI 上搜索:

      UI thread blocked 0.730504035949707
      

      用上面的代码搜索:

      UI thread blocked 0.28138411045074463
      background search duration 0.5073530673980713
      

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-18
      • 1970-01-01
      • 2016-02-26
      • 2014-10-04
      • 1970-01-01
      相关资源
      最近更新 更多