后台线程上的领域通知令牌

2024-01-29

我试图在后台线程上获取领域数据并添加通知块(iOS,Swift)。

基本示例:

    func initNotificationToken() {

        DispatchQueue.global(qos: .background).async {
          let realm = try! Realm()
          results = self.getRealmResults()

        notificationToken = results.addNotificationBlock { [weak self] (changes: RealmCollectionChange) in

          switch changes {
           case .initial:
            self?.initializeDataSource()
            break
          case .update(_, let deletions, let insertions, let modifications):
           self?.updateDataSource(deletions: deletions, insertions: insertions, modifications: modifications)
           break
          case .error(let error):
            fatalError("\(error)")
            break
          }
        }
      }
    }

    func initializeDataSource() {
          // process the result set data 

         DispatchQueue.main.async(execute: { () -> Void in
            // update UI
         })
    }

    func updateDataSource(deletions: [Int], insertions: [Int], modifications: [Int]) {
          // process the changes in the result set data 

         DispatchQueue.main.async(execute: { () -> Void in
            // update UI
         })
    }

当这样做时我得到

'Can only add notification blocks from within runloops'

我必须对返回的数据进行一些更广泛的处理,并且只想在处理完成后更新 UI 时返回主线程。

另一种方法可能是在后台线程上进行任何更新后重新获取数据,然后进行处理,但这感觉是可以避免的开销。

关于解决此问题的最佳实践有什么建议吗?


要在后台线程上添加通知,您必须在该线程上手动运行运行循环,并从该运行循环的标注中添加通知:

class Stuff {
    var token: NotificationToken? = nil
    var notificationRunLoop: CFRunLoop? = nil

    func initNotificationToken() {
        DispatchQueue.global(qos: .background).async {
            // Capture a reference to the runloop so that we can stop running it later
            notificationRunLoop = CFRunLoopGetCurrent()

            CFRunLoopPerformBlock(notificationRunLoop, CFRunLoopMode.defaultMode.rawValue) {
                let realm = try! Realm()
                results = self.getRealmResults()

                // Add the notification from within a block executed by the
                // runloop so that Realm can verify that there is actually a
                // runloop running on the current thread
                token = results.addNotificationBlock { [weak self] (changes: RealmCollectionChange) in
                    // ...
                }
            }

            // Run the runloop on this thread until we tell it to stop
            CFRunLoopRun()
        }
    }

    deinit {
        token?.stop()
        if let runloop = notificationRunLoop {
            CFRunLoopStop(runloop)
        }
    }
}

GCD 在其工作线程上不使用运行循环,因此任何基于将块分派到当前线程的运行循环(例如 Realm 的通知)的内容都不会被调用。为了避免静默通知失败,Realm 试图检查这一点,不幸的是,这需要令人惊讶的 PerformBlock 舞蹈。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

后台线程上的领域通知令牌 的相关文章

随机推荐