Notes on NSNotificationCenter

This blog is sometimes about content from my head, but also a place to just keep interesting reference info.

I was debugging a crash that provided very little info about my app’s code, but threw an exception when calling

[UIViewController didPurgeMemoryForReason:]

In the end I suspect it was because someone was calling

[[NSNotificationCenter defaultCenter] removeObserver: self];

outside of a dealloc method, which ends up accidentally deregistering for the didReceiveMemoryWarning notification.

Anyway, looking at this post, in the comments, I learned something new about a block-based API for the notification center where you can provide the notification’s handler right as you add the observer. What’s more is how you remove the observer.

I quote shusta from the comments:

NSNotificationCenter provides -addObserverForName:object:queue:usingBlock: which, unlike the other addObserver methods, returns an actual observer. To stop observing, you call -removeObserver: with that returned object instead of self. This isn’t as weird as it might sound.

To set up the observer, you would call

id keyboardWillShowObserver = [[NSNotificationCenter defaultCenter] 
    addObserverForName:UIKeyboardWillShowNotification
                object:nil
                 queue:nil
            usingBlock:^(NSNotification *notification) {

// react to notification
}];

This tells the notification center to call the provided block upon UIKeyboardWillShowNotification, and returns to you keyboardWillShowObserver. (NSNotificationObserver retains keyboardWillShowObserver. You probably don’t need to.)

To stop observing UIKeyboardWillShowNotification, you would call

[[NSNotificationCenter defaultCenter] removeObserver:keyboardWillShowObserver];
keyboardWillShowObserver = nil;

So now you don’t have to worry about side effects such as superclass observers, since you’re not removing your entire object as an observer.

This isn’t all sparkly pixie dust, though. Block-based observers come with their own problems, which is that blocks probably retain self. So if you want to remove the observer in -dealloc, you’ll have a problem because -dealloc will not be called. (A retain cycle)

The way to solve this is to create a block variable (for primitives), or a weak pointer to self and use that instead.

__weak MyClass *weakself = self;
/***/ ^(NSNotification *notification) {
    [weakself->array removeAllObjects];
    [weakself showKeyboardLabel];
}];

The block won’t retain weakself, and you can -removeObserver: in -dealloc, no problem.

Of course, if you’re going to removing the observer sooner, it’s probably unnecessary. Regardless, the block based method avoids the pitfall described in this article by removing observers very precisely instead of indiscriminately.

Advertisements