我们可以在后台队列中生成 Realm 结果并在主线程上使用它吗

Posted

技术标签:

【中文标题】我们可以在后台队列中生成 Realm 结果并在主线程上使用它吗【英文标题】:Can we generate Realm results in background queue and use it on main thread 【发布时间】: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”而终止应用程序,原因:“从错误线程访问的领域。

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

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

谢谢。

【问题讨论】:

嘿,你能弄清楚这个吗?似乎不可能将全新的领域对象从后台移动到主线程...我只看到从主线程转到后台的示例... 【参考方案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

【讨论】:

使用 Swift 在后台线程上的领域通知 - academy.realm.io/posts/…

以上是关于我们可以在后台队列中生成 Realm 结果并在主线程上使用它吗的主要内容,如果未能解决你的问题,请参考以下文章

android中生成excel

如何在后台队列中获取对象的领域结果,然后在主线程上使用它

如何比较多列,并在单个新列中生成值,在 Pandas 中使用 Apply 函数

android中生成excel

在 MVC 中生成组织图并在视图中显示

我们是不是有任何库可以从表中生成类中的实体字段?