2017-01-24 13 views
4

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

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

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

+0

一些代码和使用的语言将是有用的 –

+0

谢谢@Luca。添加了我正在尝试做的事情的语言和基本示例。 – Nechriz

回答

6

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

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需要跳舞。

+0

我试图整合您的代码以从Synced领域获取结果。但我得到这个错误'RLMException',原因:'从不正确的线程访问的领域' – Jeyachandran

+0

@Thomas Goyne我得到了这个设置,但它只适用于当我的应用程序在前台。有任何想法吗? –