Reputation: 1934
I have a notification posted in the main thread, and I want to exec something in the background after I receiving that notification.
So I use this method with a background OperationQueue
func addObserver(forName name: NSNotification.Name?,
object obj: Any?,
queue: OperationQueue?,
using block: @escaping (Notification) -> Void) -> NSObjectProtocol
I thought this should work, but it doesn't.
I have read about the doc, there are something I don't understand about the queue, it says:
queue
The operation queue to which block should be added.
If you pass nil, the block is run synchronously on the posting thread.
so if we pass nil, the block will run synchronously on the posting thread, but what about if we pass a queue
, does it still run synchronously on the posting thread?
I have wrote some code to test my thinking,
import Foundation
let queue = OperationQueue()
let testNotification = Notification.Name("testNotification")
NotificationCenter.default.addObserver(forName: testNotification, object: nil, queue: queue) { _ in
print("receive notification: before sleep.")
Thread.sleep(forTimeInterval: 2)
print("receive notification: after sleep.")
}
NotificationCenter.default.post(name: testNotification, object: nil)
print("main thread")
RunLoop.main.run()
the output was:
receive notification: before sleep.
receive notification: after sleep.
main thread
so the block does run synchronously on the posting thread.
My question was, what's the point of this method, and what's the point of the params queue
, when should we use it?
Upvotes: 3
Views: 2918
Reputation: 1871
I added:
import Foundation
let queue = OperationQueue()
let testNotification = Notification.Name("testNotification")
NotificationCenter.default.addObserver(forName: testNotification, object: nil,
queue: queue) { _ in
if queue == OperationQueue.current { print("I am in my queue") }
print("receive notification: before sleep.")
Thread.sleep(forTimeInterval: 2)
print("receive notification: after sleep.")
}
NotificationCenter.default.post(name: testNotification, object: nil)
print("main thread")
This prints out:
I am in my queue
receive notification: before sleep.
receive notification: after sleep.
main thread
We can tell it is running the using
block in the OperationQueue queue
.
My understanding is the NotificationCenter.post
method will wait until the using
block is complete (synchronously) regardless of what queue it runs the using
block in. There is a NotificationQueue buffer class that gives the functionality you are looking for.
NotificationCenter.default.addObserver(forName: testNotification, object: nil,
queue: queue) { _ in
print("receive notification: before sleep.")
Thread.sleep(forTimeInterval: 2)
print("receive notification: after sleep.")
}
NotificationQueue.default.enqueue(Notification(name: testNotification),
postingStyle: .whenIdle)
print("main thread")
Prints out:
main thread
receive notification: before sleep.
receive notification: after sleep.
I think the reason for the queue
parameter is if using a serial queue for access control, one could pass in the queue
to use. For example the UIKit uses the main
queue to serialize all screen manipulation.You could do something like
NotificationCenter.default.addObserver(forName: testNotification, object: nil,
queue: OperationQueue.main) { _ in
// Do a bunch of UI stuff
}
So when you update your model from a background queue, you can post a notification to the controller to update the views, and guarantee the UIKit manipulation is done in the main
queue.
Upvotes: 4