首页 > 解决方案 > 在后台线程中对 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)
    }

标签: swiftrealm

解决方案


正如@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)
    }

推荐阅读