领域通知令牌在背景线程上

时间:2017-01-24 03:16:04

标签: ios swift realm

我试图在后台线程上获取领域数据并添加一个通知块(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时返回主线程。

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

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

1 个答案:

答案 0 :(得分:6)

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

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

相关问题