Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 751
Sudhanshu Gupta Avatar asked Jun 05 '16 09:06

Sudhanshu Gupta


2 Answers

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){
    }
}
like image 64
Sudhanshu Gupta Avatar answered Sep 19 '22 18:09

Sudhanshu Gupta


You should have a boolean flag which you should set as true when observer is added and set it false on removing. Add observer only when this flag is false. Also add check in viewWillDisappear before removing observer. Also add log

    if (self.isMovingFromParentViewController || self.isBeingDismissed) {
        if (isReportKVOAdded) {
            [self removeObserver:self forKeyPath:@"dataContainer.report" context:nil];
        }
    }
like image 36
Atif Avatar answered Sep 21 '22 18:09

Atif