我们可以在后台队列中生成 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 结果并在主线程上使用它吗的主要内容,如果未能解决你的问题,请参考以下文章