NSNotificationCenter and Secure Multithreading

Given that objects can be freed even when the method is called ( link ) *, is it safe for the object to register and receive notifications that will be delivered in a stream other than the one on which it is calculated to be freed?

For reference, the documentation states that

In a multi-threaded application, notifications are always delivered in the stream in which the notification was sent, which may not be the same stream in which the observer registered himself.

It is also important that the NSNotificationCenter does not contain references to objects registered to receive notifications.

Here is an example that can make the situation more specific:

- (id)init { self = [super init]; if (self) { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:SomeNotification object:nil]; } } - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } - (void)handleNotification:(NSNotification *)notification { // do something } 

An object with this implementation receives SomeNotification in thread X. Before -handleNotification: returns, the last strong reference to the object (in the code that I see) is broken.

  • I understand correctly that:

    but. If the NSNotificationCenter refers to an object before calling -handleNotification: on it, the object will not be freed until after -handleNotification: return and

    b. if NSNotificationCenter does not refer to the object before calling -handleNotification: on it the object can be freed before -handleNotification: returns

  • Which method (a or b) works? I have not found this topic in the documentation yet, but it seems somewhat important to use NSNotificationCenter safely in a multi-threaded environment.

UPDATE:. The answer in the above link was updated, indicating that "ARC stores and releases around the call when the link is weak." This means that the object should not be freed when the method is called.

+8
multithreading objective-c nsnotificationcenter
source share
2 answers

I always recommend that if you see notifications that fly around threads other than the main one, and you see that maladaptation occurs in the background, your streaming may be too complicated. ObjC is not a no-man's language. Most thread operations should be in the form of short-lived blocks in queues. They can send notifications back to the main thread easily, but they should not use notifications often.

However, the best way to manage multi-threaded notifications is addObserverForName:object:queue:usingBlock: This can significantly increase the lifetime. The template should look something like this:

 __weak id weakself = self; id notificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:... object:... queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note){ id strongself = weakself; if (strongself) { [strongself handleNotification:note]; } }]; 

Pay attention to the use of the weak / oneself. We avoid a closed loop using weakness. When we get back, first take a strong link. If we still exist, then we are locked for the rest of the block (therefore, we cannot dealloc in handleNotification: . If we do not exist, the notice is discarded. (Note that weak links are actually nullified before calling dealloc . See objc_loadWeak .) I use mainQueue here, but you can, of course, use a different queue.

In the "old days" (before 10.6), I developed this problem by controlling the lifetime of the object. In principle, I designed such that short-lived objects did not listen to notifications that might arise from other threads. It was much simpler than it seems, because in code up to 10.6 streams can be stored quite rarely (and IMO, nevertheless, needs to be kept low). NSNotificationCenter was designed for this low-flow world.

Also note that unlike -[NSNotificationCenter addObserver:selector:name:object:] , -[NSNotificationCenter addObserverForName:object:queue:usingBlock:] returns an opaque object that acts as an observer. You must track this object so that you can remove the observer later.

+8
source share

NSNotificationCenter does not use a significant object reference, so the observer must be removed before being released. When ARC is enabled, the observer will not be released when handleNotification is called, since calling handleNotification will increase the save counter. If the observer is freed before the notification is published, the NSNotificationCenter will remove it from the observers when writing to the dealloc method, so that handleNotification will not be called. NSNotificationCenter synchronously calls notification handlers when a notification is sent.

0
source share

All Articles