Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 951
warpedspeed Avatar asked Dec 12 '11 16:12

warpedspeed


People also ask

How do I delete an observer in Objective C?

When removing an observer, remove it with the most specific detail possible. For example, if you used a name and object to register the observer, use removeObserver:name:object: with the name and object.

Should I remove observer Swift?

Why we should remove observer, Yes, we don't have to remove observers from From OS X 10.11 and iOS 9.0. This is not applicable for block based approach. Even when you use [weak self] remove the observer (or) make sure you never use shared instances within this closure.

Do I need to removeObserver?

specifies. If your app targets iOS 9.0 and later or macOS 10.11 and later, and you used addObserver(_:selector:name:object:) , you do not need to unregister the observer. If you forget or are unable to remove the observer, the system cleans up the next time it would have posted to it.


2 Answers

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.

like image 169
Robotic Cat Avatar answered Sep 20 '22 15:09

Robotic Cat


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];
}];
like image 29
matt Avatar answered Sep 18 '22 15:09

matt