iOS KVO - Cannot remove an observer

13,346

Solution 1

There's no guarantee that a viewDidAppear will be matched with a viewWillDisappear every time. This means your KVO registration/unregistration would potentially be unbalanced and non-deterministic. You should perform KVO registration/unregistration in guaranteed pairings like viewDidLoad and dealloc.

Solution 2

Apple Docs say there is a way to add observer when view is visible only. According to Figure 1 - Valid State Transitions you can use pair viewWillAppear/viewWillDisppear for adding and removing observers. At the same time you can use init/dealloc pair, but not viewDidLoad/dealloc - view can be not loaded, but controller deallocated.

Your code should be:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    [self addObserver:self forKeyPath:@"importStuff" options:0 context:NULL];
    [self addObserver:self forKeyPath:@"importStuffFailed" options:0 context:NULL];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];

    [self removeObserver:self forKeyPath:@"importStuff"];
    [self removeObserver:self forKeyPath:@"importStuffFailed"];
}
Share:
13,346

Related videos on Youtube

j2emanue
Author by

j2emanue

A mobile developer of both IOS and Android platforms

Updated on June 30, 2022

Comments

  • j2emanue
    j2emanue almost 2 years

    I have a simple Viewcontroller that is KVO compliant and has the following in it:

      - (void) viewDidAppear:(BOOL)animated
        {
            [super viewDidAppear:animated];
    
    
    
            [self addObserver:self forKeyPath:@"importStuff" options:0 context:NULL];
            [self addObserver:self forKeyPath:@"importStuffFailed" options:0 context:NULL];
            }
    
        - (void) viewWillDisappear:(BOOL)animated
        {
            [super viewWillDisappear:animated];
    
            [self removeObserver:self forKeyPath:@"importStuff"];
            [self removeObserver:self forKeyPath:@"importStuffFailed"];
          }
    

    the issue im having is that sometimes user are reporting the following error:

    Cannot remove an observer <MyViewController 0x145d0c8d0> for the key path "importStuff" from <MyViewController 0x1741b2280> because it is not registered as an observer.
    

    the addObserver call is not called anywhere else in code. is it something about the life cycles im missing ? isn't viewDidAppear guaranteed to be called once (so it should register the keys right ?)

    • luk2302
      luk2302 almost 9 years
      @vib no no no no. NO. Do I have to explain that that is really very very bad practice? That does not solve anything and will just cause your program to not work as intended and crash at some later point without indication to why. Not logging exceptions is a very bad idea as well. An exception is an indication that something went wrong that you should take care of and not just ignore.
    • gurooj
      gurooj almost 9 years
      No, there's no guarantee that a viewDidAppear will be matched with a viewWillDisappear every time. It is very bad practice to handle KVO in these methods. Do not attempt! Do it in more documented pairings like viewDidLoad/dealloc
    • gurooj
      gurooj
      This is not a safe way to handle KVO. If your app is interrupted by a phone call, then viewWillDisappear won't get called, but viewDidAppear could get called again when the app relaunches. There are many other less than ideal possibilities. You should consider using more reliable methods to add and remove observers (viewDidLoad, dealloc, etc)