领域通知令牌在后台线程
我试图在后台线程上获取领域数据并添加通知块(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时只想回到主线程。
另一种方法可能是在后台线程更新之后重新获取数据,然后进行处理,但感觉像是可以避免的开销。
有关解决此问题的最佳做法的任何build议?
要在后台线程上添加通知,您必须在该线程上手动运行一个运行循环,并从该运行循环的调用中添加通知:
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需要跳舞。