Why doesn't Remove Observer from NSNotificationCenter:addObserverForName:usingBlock get called
Asked Answered
C

2

13

I'm confused on why the observer is never removed in the following code. In my viewDidAppear I have the following:

-(void)viewDidAppear:(BOOL)animated{

id gpsObserver = [[NSNotificationCenter defaultCenter] 
                          addObserverForName:FI_NOTES[kNotificationsGPSUpdated] 
                          object:nil 
                          queue:[NSOperationQueue mainQueue] 
                          usingBlock:^(NSNotification *note){

                              NSLog(@"run once, and only once!");

                [[NSNotificationCenter defaultCenter] removeObserver:gpsObserver];

        }];

}

The observer never gets removed and the statement is output every time the notification is sent out. Can anyone provide any guidance?

Chaisson answered 12/12, 2011 at 16:41 Comment(0)
B
30

When the block is pushed onto the stack by addObserverForName: the method has not yet returned so gpsObserver is nil (under ARC) or garbage/undefined (not under ARC). Declare the variable using __block outside and this should work.

__block __weak id gpsObserver;

gpsObserver = [[NSNotificationCenter defaultCenter] 
                          addObserverForName:FI_NOTES[kNotificationsGPSUpdated] 
                          object:nil 
                          queue:[NSOperationQueue mainQueue] 
                          usingBlock:^(NSNotification *note){

                              NSLog(@"run once, and only once!");

                [[NSNotificationCenter defaultCenter] removeObserver:gpsObserver];

        }];

I've added an __weak to ensure there is no memory leak (as per Matt's answer). Code not tested.

Bend answered 12/12, 2011 at 17:3 Comment(1)
That makes sense and works as expected; thank you for your help.Chaisson
A
11

I find that in fact there is a memory leak unless the observer is marked both __block and __weak. Use Instruments to make sure that self is not being overretained; I bet it is. This, however, works correctly (from my actual code):

__block __weak id observer = [[NSNotificationCenter defaultCenter] 
    addObserverForName:@"MyMandelbrotOperationFinished" 
    object:op queue:[NSOperationQueue mainQueue] 
    usingBlock:^(NSNotification *note) {
        // ... do stuff ...
        [[NSNotificationCenter defaultCenter] 
            removeObserver:observer 
            name:@"MyMandelbrotOperationFinished" 
            object:op];
}];
Acetous answered 16/2, 2012 at 0:15 Comment(2)
I'd love it if they'd introduce the equivalent method that would actually not require you to persist the observers but rather use yourself as one, simply using blocks instead of selectors.Patriarchy
You don't need to make observer __weak if you set it to nil after removing it from NSNotificationCenter. I've checked this with Instruments.Militia

© 2022 - 2024 — McMap. All rights reserved.