在后台线程中对 Realm Results 对象进行排序

Sorting a Realm Results object in the background thread

我正在尝试在后台线程中对 Realm Results 实例进行排序。但是我收到“从不正确的线程访问的领域。”异常。我在这里做错了什么?

我正在使用此函数来过滤和更新 table,结果是搜索栏文本字段中的文本发生变化。

提前致谢。

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 {
                    [=10=].name!.levenshteinDistance(searchedTerm) < .name!.levenshteinDistance(searchedTerm)
                }
                
            } else {  // searching by id
                // ...
            }
            completion(filteredInvoices)
        }
        previousSearchWork = newSearchWork
        DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: .now() + .milliseconds(30), execute: newSearchWork)
    }

正如@Jay 在对原始问题的回复中提到的:

... that Realm is on a background thread so the objects are on that thread; what happens with [Invoice] upon completion?

是的,事实证明我一直在 后台线程 上获取 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: [=10=]) }
        
        mainThread.async {
            let mainRealm = try! Realm()
            let myObjectsArray: [MyObject?] = references.map { mainRealm.resolve([=10=]) }
        }
    }
    
    // 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 { [=10=].id }
        
        mainThread.async {
            let mainRealm = try! Realm()
            let myObjectsArray: [MyObject?] = keys.map { mainRealm.object(ofType: MyObject.self, forPrimaryKey: [=10=]) }
        }
    }

调整功能后(根据我的需要完成):

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 {
                        [=11=].name!.levenshtein(searchedTerm) < .name!.levenshtein(searchedTerm)
                    }.map {[=11=].id}
                    
                } else {
                    var predicates = [NSPredicate(format: "%@ IN ticket.pattern.sequences", searchedTerm)]
                    
                    if searchedTerm.count > 3 {
                        let regex = searchedTerm.charactersSorted().reduce("*") {[=11=] + "\()*"}
                        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 {[=11=].id})
                }
                DispatchQueue.main.async {
                    completion(filteredIDs)
                }
            }
        }
        previousSearchWork = newSearchWork
        DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: .now() + .milliseconds(30), execute: newSearchWork)
    }