iosobjective-cmemorykey-value-observingdealloc

key value observers were still registered with it when controller is deallocated


I added an observer in the code and then removed it in dealloc and viewWillDisappear but still i am getting an error stating

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'An instance 0x167e5980 of class MyController2 was deallocated while key value observers were still registered with it.

Current observation info: <NSKeyValueObservationInfo 0x16719f90> (
<NSKeyValueObservance 0x16719fb0: Observer: 0x167e5980, Key path: dataContainer.report, Options: <New: YES, Old: YES, Prior: NO> Context: 0x0, Property: 0x1677df30>
)'

I created a controller, MyController and derive a new controller MyController2 from it. Now i added KVO in MyController2.

- (void)viewDidLoad {
    [super viewDidLoad];
    [self addObserver:self forKeyPath:@"dataContainer.report" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
}

Then in observeValueForKeyPath :-

- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {

    id oldC = [change objectForKey:NSKeyValueChangeOldKey];
    id newC = [change objectForKey:NSKeyValueChangeNewKey];

    if([keyPath isEqualToString:@"dataContainer.report"]) {
        if (oldC != newC) {
            //Remove Observer

            [self removeObserver:self forKeyPath:@"dataContainer.report" context:nil];
            [self updateDataContainer];
            [self reportView];
        }
    }
}

Then i tried to remove observer in viewWillDisappear and dealloc both :-

- (void)dealloc {
    @try{
        [self removeObserver:self forKeyPath:@"dataContainer.report" context:nil];
    }@catch(id anException){
    }
}

-(void) viewWillDisappear:(BOOL)animated{
    @try{
        [self removeObserver:self forKeyPath:@"dataContainer.report" context:nil];
    }@catch(id anException){
    }
    [super viewWillDisappear:animated];
}

I looked at lost of posts , all of them say one thing you need to remove observer. I tried to remove observer from both of them but still i am getting the issue.


Solution

  • From my experience best way to add and remove observer in iOS.

    Add observer in ViewDidLoad:-

    - (void)viewDidLoad {
        [super viewDidLoad];
        [self addObserver:self forKeyPath:@"dataContainer.report" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
    }
    

    To observe the observer we have to do this:-

    Don't remove observer in observeValueForKeyPath

    - (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    
        id oldC = [change objectForKey:NSKeyValueChangeOldKey];
        id newC = [change objectForKey:NSKeyValueChangeNewKey];
    
        if([keyPath isEqualToString:@"dataContainer.report"]) {
            if (oldC != newC) {
                [self updateDataContainer];
                [self reportView];
            }
        }
    }
    

    Remove Observer In dealloc :

    call remove once here

    - (void)dealloc {
        @try{
            [self removeObserver:self forKeyPath:@"dataContainer.report" context:nil];
        }@catch(id anException){
        }
    }