removeObserver not working

2019-03-20 18:05发布

问题:

I have next code:

@implementation SplashViewVC

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.splashView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"Default.png"]];
    self.activityIndicator.originY = 355.f;
    [[NSNotificationCenter defaultCenter] addObserverForName:NCDownloadComplete object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *n){
        NSInteger errorCode = [n.userInfo[@"errorCode"] integerValue];        
        [self.activityIndicator stopAnimating];
        if (errorCode == ERROR_CODE_NO_CONNECTION) {
            UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error" message:@"Some problem with server" delegate:self cancelButtonTitle:@"try again" otherButtonTitles:nil];
            [alertView show];
        } else if (errorCode == 0) {
            [self dismissViewControllerAnimated:YES completion:nil];
        }
    }];
    [self downloadData];
}

- (void)downloadData
{
    [self.activityIndicator startAnimating];
    [[Server sharedServer] getMovieData];
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    [self downloadData];
}

- (void)viewDidDisappear:(BOOL)animated
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [super viewDidDisappear:animated];
}

@end

So I put breakpoints in begin of viewDidLoad method, in viewDidDisappear. When I launch app that first go to viewDidload, after downloading it is go to viewDidDisappear.

But during my app I again download data and post notification: NSDownloadComplete. And in this VC it is work, but I removed later using:

[[NSNotificationCenter defaultCenter] removeObserver:self]

This VC use viewDidLoad once in the beginning & can not again addObserver.

What is wrong?

EDIT I try put addObserver method to viewWillAppear or viewWillDisappear - no results. I add NSLog(@"addObserver"); before

 [[NSNotificationCenter defaultCenter] addObserverForName...

in viewDidLoad

and write

- (void)viewDidDisappear:(BOOL)animated
{
    NSLog(@"removeObserver");
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [super viewDidDisappear:animated];
}

In log I see:

2013-06-10 14:32:05.646 myApp[9390:c07] addObserver
2013-06-10 14:32:06.780 myApp[9390:c07] removeObserver

What wrong?

EDIT 2 you can see that observer must be removed but it again run block in addObserver method

回答1:

Apart from add/remove observer calls not properly being balanced, at noted in the other answers, there is another problem.

Your code to remove the observer is wrong. For a block-based observer, the return value of addObserver must be given as argument to removeObserver. So you should add a property

@property(nonatomic, strong) id observer;

to the class. Then you add the observer with

self.observer = [[NSNotificationCenter defaultCenter] addObserverForName:NCDownloadComplete object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *n){
    // ...
}];

and remove it with

[[NSNotificationCenter defaultCenter] removeObserver:self.observer];


回答2:

What e1985 is trying to expose is that your addObserver and removeObserver calls are not properly balanced. viewDidLoad is called only once after the VC initialization, but viewDidDisappear is called each time the view controller is moved off screen.

To resolve your issue you must balance your addObserver and removeObserver calls, either by making them in viewDidLoad and the other in dealloc, or - as e1985 suggested - in viewDidAppear: and viewDidDisappear:.

EDIT: Ok, so your problem comes from the fact that you are using addObserverForName:object:queue:usingBlock: which do not register self as observer (as addObserver:selector:name:object: would do if you pass self as first argument).

So in your case, [[NSNotificationCenter defaultCenter] removeObserver:self]; does nothing because self is not an observer. You should instead call removeObserver: on the return value of addObserverForName:object:queue:usingBlock:, as shown in the doc:

Return Value

An opaque object to act as the observer.

So your code should looks something like:

// header file .h
@interface SplashViewVC : UIViewController

@property (strong, nonatomic) id downloadCompleteObserver;

@end

// implementation file .m
@implementation SplashViewVC

- (void)viewDidLoad
{
    [super viewDidLoad];

    // [...] snip

    self.downloadCompleteObserver = [[NSNotificationCenter defaultCenter] addObserverForName:NCDownloadComplete object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *n){
        NSInteger errorCode = [n.userInfo[@"errorCode"] integerValue];        
        [self.activityIndicator stopAnimating];
        if (errorCode == ERROR_CODE_NO_CONNECTION) {
            UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error" message:@"Some problem with server" delegate:self cancelButtonTitle:@"try again" otherButtonTitles:nil];
            [alertView show];
        } else if (errorCode == 0) {
            [self dismissViewControllerAnimated:YES completion:nil];
        }
    }];
    [self downloadData];
}

// [...] snip

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

@end


回答3:

The pattern you are using is not correct. You should add the observer in viewDidAppear: and remove it in viewDidDisappear:.