9 posts tagged with Blocks

Be careful using NSNotificationCenter with Blocks

NSNotificationCenter is a long existing mechanism for broadcasting messages to zero or many listeners. Many of Apple’s frameworks work deeply by notifiying you via an NSNotification when a message is posted. Traditionally, the workflow has been to follow a pattern similar to this:

- (void)viewDidLoad 
{
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(someMethod:) 
                                                 name:kMyNotificationIdentifier 
                                               object:nil];
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)someMethod:(NSNotification *)note
{
    // Message received
}

Blocks!

As of iOS 4 and the introduction of blocks, Mac and iOS developers can now use blocks to subscribe to NSNotification broadcasts. This new method also allows you to specify an NSOperationQueue to perform the block action on.

- (void)viewDidLoad
{
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserverForName:kMyNotificationIdentifier 
                                                      object:nil 
                                                       queue:nil
                                                  usingBlock:^(NSNotification *note) {
        // message received
    }];
}

Wait, what?

Not so fast! Who is the observer? What removes this observer? What if you load several view controllers that do this? This block based method is not as simple as it appears. Reading the docs, we learn that addObserverForName:object:queue:usingBlock: actually returns an opaque observer that you are meant to retain, and subsequently -removeObserver with. Let’s take a look at what this looks like.

@implementation MyViewController
{
    id _notificationObserver;
}

// ...

- (void)viewDidLoad
{
    [super viewDidLoad];
    _notificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMyNotificationIdentifier 
                                                                              object:nil 
                                                                               queue:nil
                                                                          usingBlock:^(NSNotification *note) {
        // message received
    }];
}

// dealloc, or potentially a method popping this view from the stack
- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:_notificationObserver];
}

As you can see, you still need to track the observer of a notification and remove it. Similar to what you would do if you were using the selector-based notification listener. Oddly enough, this is an example of a block-based API not really improving things. For this API, the selector-based NSNotificationCenter listener is a much simpler option as you don’t have to maintain the observer seperately.