NSNotificationCenter is thread-safe NOT NOT

July 24, 2017

By Jeff Johnson

Nostalgia is now the norm. (Norm!) Since nobody is expected to have original ideas anymore, I thought I would revisit an old blog post of mine: NSNotificationCenter is thread-safe NOT. That post discussed the thread safety of NSNotificationCenter and raised the problem of an object getting deallocated on one thread while it was observing a notification posted on another thread. I've decided it's important to follow up on my old blog post, because the problem it raised has been solved.

In the past few years, NSNotificationCenter has seen several improvements. I'll quote a passage from the Foundation release notes for OS X 10.11:

In OS X 10.11 and iOS 9.0 NSNotificationCenter and NSDistributedNotificationCenter will no longer send notifications to registered observers that may be deallocated. If the observer is able to be stored as a zeroing-weak reference the underlying storage will store the observer as a zeroing weak reference, alternatively if the object cannot be stored weakly (i.e. it has a custom retain/release mechanism that would prevent the runtime from being able to store the object weakly) it will store the object as a non-weak zeroing reference. This means that observers are not required to un-register in their deallocation method. The next notification that would be routed to that observer will detect the zeroed reference and automatically un-register the observer. If an object can be weakly referenced notifications will no longer be sent to the observer during deallocation; the previous behavior of receiving notifications during dealloc is still present in the case of non-weakly zeroing reference observers.

This passage doesn't directly address our previous problem, because we were not concerned with notifications sent during or after deallocation. Rather, we were concerned with deallocation after a notification was already sent but before it has finished processing. The good news is that NSNotificationCenter seems to have solved both of these problems. As far as I can tell, NSNotificationCenter now keeps a strong reference to the observer while posting a notification. As a consequence, the observer can no longer deallocate while processing the notification. This behavior occurs both with Automatic Reference Counting (ARC) and with Manual Retain-Release (MRR). Hooray!

It's still true that removeObserver: does not block for notifications in progress. In other words, removeObserver: can return on one thread while a notification method is still running on another thread. However, this will not occur in dealloc, so it should generally be a non-issue, or at least less of an issue than before. Thus, NSNotificationCenter has become significantly more thread-safe, which is good news.