领域通知令牌在背景线程上 [英] Realm notification token on background thread

查看:105
本文介绍了领域通知令牌在背景线程上的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I was trying to fetch realm data on the background thread and add a notification block (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时返回主线程。

I have to do some more extensive processing with the returned data and would like to only go back to the main thread when updating the UI after the processing is done.

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

Another way would probably to re-fetch the data after any update on the background thread and then do the processing, but it feels like avoidable overhead.

有关解决此问题的最佳做法的任何建议吗?

Any suggestions on the best practice to solve this?

推荐答案

添加通知在后台线程上,你必须在该线程上手动运行一个运行循环,并从该运行循环的callout中添加通知:

To add a notification on a background thread you have to manually run a run loop on that thread and add the notification from within a callout from that run loop:

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舞蹈。

GCD does not use a run loop on its worker threads, so anything based on dispatching blocks to the current thread's run loop (such as Realm's notifications) will never get called. To avoid having notifications silently fail to do anything Realm tries to check for this, which unfortunately requires the awakward PerformBlock dance.

这篇关于领域通知令牌在背景线程上的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆