iOS 11. What the KVO_IS_RETAINING_ALL_OBSERVERS_OF_THIS_OBJECT_IF_IT_CRASHES_AN_OBSERVER_WAS_OVERRELEASED_OR_SMASHED is mean?

Mikhail S picture Mikhail S · Oct 17, 2017 · Viewed 9.6k times · Source

In the new iOS11, I get some strange exceptions. I do not understand why this is happening. In the previous iOS, there was no such exception. Log attached:

Who ever encountered this? What is it and how to defeat it?

Answer

Alex Wally picture Alex Wally · Oct 25, 2017

It's related to KVO = Key-Value Observing. Check if you're calling the function

object.addObserver(self, forKeyPath:..., options:..., context:...)

somewhere; that's your KVO observer. This class would also override the function

observeValue(forKeyPath:of:change:context:)

As the error message says, if you get a crash here, that means the observer was "overreleased" or "smashed". I think that just means it was released while still observing the key path.

How to fix it?

Swift 3

If you need to fix it in Swift 3 (as I did), make sure you call removeObserver once you are no longer interested in observing the key path. The easiest way to do that is to add a deinit method to your observer:

deinit {
    object.removeObserver(self, forKeyPath:#keyPath(same.as.in.addObserver))
}

Make sure to replace object and the key path with the same references you used in addObserver!

More info: https://cocoacasts.com/key-value-observing-kvo-and-swift-3/

Swift 4 / iOS

From Swift 4 / iOS 11 you can use blocks, as in this question: In Swift 4, how do I remove a block-based KVO observer?

Instead of using the observeValue method, you can just add the observer like so:

var observer: NSKeyValueObservation? = foo.observe(.value, options: [.new]) { (foo, change) in
   print(change.newValue)     // whatever needs to happen when the value changes
}

In iOS, you should still keep a reference to the observer and call invalidate on it at an appropriate time, e.g. in deinit or in viewWillDisappear.

Swift 4 / macOS

If you're developing for macOS 10.13 or newer, under certain conditions they no longer need to be removed. Quote from the docs:

Relaxed Key-Value Observing Unregistration Requirements

Prior to 10.13, KVO would throw an exception if any observers were still registered after an autonotifying object's -dealloc finished running. Additionally, if all observers were removed, but some were removed from another thread during dealloc, the exception would incorrectly still be thrown. This requirement has been relaxed in 10.13, subject to two conditions:

  • The object must be using KVO autonotifying, rather than manually calling -will and -didChangeValueForKey: (i.e. it should not return NO from +automaticallyNotifiesObserversForKey:)
  • The object must not override the (private) accessors for internal KVO state

If all of these are true, any remaining observers after -dealloc returns will be cleaned up by KVO; this is also somewhat more efficient than repeatedly calling -removeObserver methods.

Source: https://developer.apple.com/library/archive/releasenotes/Foundation/RN-Foundation/index.html