swift - 在后台线程中对 Realm Results 对象进行排序
问题描述
我正在尝试在后台线程中对 Realm Results 实例进行排序。但是我正在从不正确的线程访问领域。' 例外。我在这里做错了什么?
当搜索栏文本字段中的文本发生变化时,我正在使用此函数来过滤和更新表格,并使用结果进行更新。
提前致谢。
var previousSearchWork?
func getInvoicesFor(searchedTerm: String, completion: @escaping ([Invoice]) -> Void) {
previousSearchWork?.cancel()
let newSearchWork = DispatchWorkItem {
guard let realm = try? Realm() else { return }
var filteredInvoices = [Invoice]()
if searchedTerm.first!.isLetter { // searching by customer name
let predicate = NSPredicate(format: "name BEGINSWITH[cd] %@ || name CONTAINS[cd] %@", searchedTerm, searchedTerm)
let invoices = realm.objects(Invoice.self).filter(predicate)
filteredInvoices = invoices.sorted {
$0.name!.levenshteinDistance(searchedTerm) < $1.name!.levenshteinDistance(searchedTerm)
}
} else { // searching by id
// ...
}
completion(filteredInvoices)
}
previousSearchWork = newSearchWork
DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: .now() + .milliseconds(30), execute: newSearchWork)
}
解决方案
正如@Jay 在对原始问题的回复中提到的那样:
... Realm 在后台线程上,因此对象在该线程上;[发票] 完成后会发生什么?
是的,事实证明我一直在后台线程上获取 Realm 持久对象,并通过完成闭包将其发送给调用者,然后调用者尝试在主线程上读取它们。这就是触发“从错误线程访问的领域”的原因
首先,由于我需要使用自定义排序方法,因此我无法找到一种方法来对对象进行排序而不将其转换为领域对象数组。
我为修复上述函数所做的只是不是返回在后台线程中获取的对象数组,而是返回对这些对象的引用,以便可以在主线程中引用它们
根据我糟糕的研究,我找到了两种将这些对象从后台线程传递到主线程的方法。(我选择了第二种方式,因为对于已阅读的内容,这种情况更快。)
let backgroundQueue = DispatchQueue.global()
let mainThread = DispatchQueue.main
// Passing as ThreadSafeReferences to objects
backgroundQueue.async {
let bgRealm = try! Realm()
let myObjects = bgRealm.objects(MyObject.self)
// ......
let myObjectsArray = .....
let references: [ThreadSafeReference<MyObject>] = myObjectsArray.map { ThreadSafeReference(to: $0) }
mainThread.async {
let mainRealm = try! Realm()
let myObjectsArray: [MyObject?] = references.map { mainRealm.resolve($0) }
}
}
// Passing primaryKeys of objects
backgroundQueue.async {
let bgRealm = try! Realm()
let myObjects = bgRealm.objects(MyObject.self)
// ......
let myObjectsArray = .....
// MyObject has a property called 'id' which is the primary key
let keys: [String] = itemsArray.map { $0.id }
mainThread.async {
let mainRealm = try! Realm()
let myObjectsArray: [MyObject?] = keys.map { mainRealm.object(ofType: MyObject.self, forPrimaryKey: $0) }
}
}
调整功能后(并根据我的需要完成):
var previousSearchWork: DispatchWorkItem?
func getInvoicesFor(searchedTerm: String, completion: @escaping ([String]) -> Void) {
previousSearchWork?.cancel()
let newSearchWork = DispatchWorkItem {
autoreleasepool {
var filteredIDs = [String]()
guard let realm = try? Realm() else { return }
let allInvoices = realm.objects(Invoice.self).filter(NSPredicate(format: "dateDeleted == nil"))
if searchedTerm.first!.isLetter {
let predicate = NSPredicate(format: "name BEGINSWITH[cd] %@ || name CONTAINS[cd] %@", searchedTerm, searchedTerm)
let invoices = allInvoices.filter(predicate)
filteredIDs = invoices.sorted {
$0.name!.levenshtein(searchedTerm) < $1.name!.levenshtein(searchedTerm)
}.map {$0.id}
} else {
var predicates = [NSPredicate(format: "%@ IN ticket.pattern.sequences", searchedTerm)]
if searchedTerm.count > 3 {
let regex = searchedTerm.charactersSorted().reduce("*") {$0 + "\($1)*"}
let predicate = NSPredicate(format: "ticket.pattern.id LIKE %@", regex)
predicates.append(predicate)
}
let invoices = allInvoices.filter(NSCompoundPredicate(orPredicateWithSubpredicates: predicates)).sorted(byKeyPath: "dateCreated", ascending: false)
filteredIDs = Array(invoices.map {$0.id})
}
DispatchQueue.main.async {
completion(filteredIDs)
}
}
}
previousSearchWork = newSearchWork
DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: .now() + .milliseconds(30), execute: newSearchWork)
}
推荐阅读
- c# - 将目标纹理保存为图像
- ipython - 在 Ipython 中分配变量的魔术方法
- mysql - mysql 5.7 是旧的吗?铁轨?
- javascript - 当我给出缩放索引时,Material-ui Popover、Select、Menus 组件位置出错
- excel - 如何将特定的 Google 电子表格转换为 Excel?
- batch-file - 批处理文件:路径上的“未定义环境变量”
- flutter - 如何使用 onGenerateRoute 在 URL 中显示 Flutter Web 路由名称?
- python - 从字符串中删除双新行
- c# - 将数据从 Ajax 传递到 ApiController
- c++ - 生成组合的虚拟按键以获得 â、ó、ć 等更复杂的字符