Pregunta

I wanted to know what are the best practices for adding and removing self as observer for some KVO property. I have added my controller object as observer for "hidden" property of a UIView. I added the observer in loadView of my view controller. Now, what is the best place to DE-register as observer for this property. I want to stop observing as soon as the view controller's view is dismissed. At times I am seeing below console warnings and at times I am crashing due to over removal as observer.

Any suggestions?

An instance 0x190659e0 of class UIView was deallocated while key value observers were still registered with it. Observation info was leaked, and may even become mistakenly attached to some other object. Set a breakpoint on NSKVODeallocateBreak to stop here in the debugger. Here's the current observation info:
¿Fue útil?

Solución

In broad terms, you need to unregister for the KVO observation when you don't need it anymore. To prevent the error you're seeing, you need to be sure you've unregistered before the observed object is deallocated.

In practical terms, that means that you really shouldn't observe something if you don't have any control over its lifecycle ie. you don't have a strong reference to it. If you do have a strong reference to it, you need to unregister before your strong reference goes away. Typically the way I handle this is to handle unregisteration on an old value and registration on the new value in a custom setter for a (strong) property referring to the object to be observed. Then, in dealloc, I also unregister my observance. Something like this:

- (void)setSomeView:(NSView *)someView
{
    if (someView != _someView) {
        [_someView removeObserver:self forKeyPath:@"someKey"];
        _someView = someView;
        [_someView addObserver:self forKeyPath:@"someKey" options:0 context:NULL];
    }
}

- (void)dealloc
{
    [_someView removeObserver:self forKeyPath:@"someKey"];
}

That way, I only observe objects that I have a strong (owning) reference to, so they can't be deallocated out from under me. And, when I'm deallocated, I also unregister for the observation.

Otros consejos

depends on when you register for it:

  • if you register in viewWilllAppear - unregister in viewWillDisappear
  • if you register in viewDidLoad / loadView - unregister in dealloc

of course there are way more options.. the idea is to find the 'counterpart method' so you registers & deregisters are balanced

For anyone who is questioning this with Swift 3.0, please see the below code which has just helped me:

override function viewDidLoad() {
super.viewDidLoad()

// some code

 webView.addObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress), options: .new, context: nil)

    }

deinit {
        webView.removeObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress))
    }

The deinit removes the observer before deallocating the WKWebview kit and changing the navigationController.

Hope this helps

Swift 4

var observer: NSKeyValueObservation?

self.observer = myPlayer.observe(\.rate, options:  [.new, .old], changeHandler: { (player, change) in
     if player.rate == 1  {
          print("Playing")
      }else{
           print("Stop")
      }
 })

 // Later You Can Remove Observer      
 self.observer?.invalidate()
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top