【问题标题】:Sorting a Realm Results object in the background thread在后台线程中对 Realm Results 对象进行排序
【发布时间】:2021-05-15 05:35:50
【问题描述】:

我正在尝试在后台线程中对 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)
    }

【问题讨论】:

  • @West1 我故意在后台线程上运行 newSearchWork 导致该函数执行一些耗时的任务。我不希望我的 ui 每次调用该函数时都没有响应。感谢您的回复:)
  • 任何时候在 objc Realm 对象上应用 Swift 过滤器时,这些对象都会从 Realm 中“断开”并且不再实时更新 Realm 对象。所以这个.sorted { $0.name! 可能是导致问题的原因。使用 Realm 排序对发票进行排序,而不是快速排序。此外,您似乎正在搜索用户输入的字符串; CONTAINS 搜索整个字符串,包括字符串的第一部分,BEGINSWITH 也搜索字符串的第一部分。你两个都需要吗?此外,该领域位于后台线程上,因此对象位于该线程上; [Invoice] 完成后会发生什么?
  • @Jay "Realm 在后台线程上,所以对象在那个线程上;完成后 [Invoice] 会发生什么?"这就是导致异常的原因。即使我在后台线程中获取它们,我也尝试在主线程上阅读它们。我已经解决了这个问题。非常感谢你。
  • @Jay。我不能使用领域排序,因为它们不能满足我的需要。例如,我通过将它们的 levenshteinDistance 与搜索的字符串进行比较来进行排序。
  • 明白。然而,重要的部分是使用 Swift 对它们进行排序将它们与 Realm 断开连接,它们将不再能够在通知中使用,并且不会实时更新 Realm 对象。很高兴你解决了这个问题。也许您可以发布编码解决方案以帮助未来的读者。

标签: swift realm


【解决方案1】:

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

... Realm 在后台线程上,因此对象在该线程上;完成后 [Invoice] 会发生什么?

是的,事实证明我一直在 后台线程 上获取 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)
    }

【讨论】:

    猜你喜欢
    • 2017-01-10
    • 1970-01-01
    • 1970-01-01
    • 2015-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多