iOS 11. What the KVO_IS_RETAINING_ALL_OBSERVERS_OF_THIS_OBJECT_IF_IT_CRASHES_AN_OBSERVER_WAS_OVERRELEASED_OR_SMASHED is mean?

11,575

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

Share:
11,575
Mikhail S
Author by

Mikhail S

Updated on June 13, 2022

Comments

  • Mikhail S
    Mikhail S about 2 years

    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:

    Crashed: com.apple.main-thread
    0  libobjc.A.dylib                0x180a5e7e8 object_isClass + 16
    1  Foundation                     0x181f013e8 KVO_IS_RETAINING_ALL_OBSERVERS_OF_THIS_OBJECT_IF_IT_CRASHES_AN_OBSERVER_WAS_OVERRELEASED_OR_SMASHED + 68
    2  Foundation                     0x181eff8ec NSKeyValueWillChangeWithPerThreadPendingNotifications + 300
    3  QuartzCore                     0x18555a6dc CAAnimation_setter(CAAnimation*, unsigned int, _CAValueType, void const*) + 156
    4  QuartzCore                     0x18555d388 -[CAPropertyAnimation setKeyPath:] + 32
    5  UIKit                          0x18a9b1a08 -[UIImageView startAnimating] + 876
    6  UIKit                          0x18a9b0e78 -[UIActivityIndicatorView startAnimating] + 48
    7  UIKit                          0x18a9b0174 -[UIActivityIndicatorView _didMoveFromWindow:toWindow:] + 212
    8  UIKit                          0x18a95845c -[UIView(Internal) _didMoveFromWindow:toWindow:] + 712
    9  UIKit                          0x18a95845c -[UIView(Internal) _didMoveFromWindow:toWindow:] + 712
    10 UIKit                          0x18a95845c -[UIView(Internal) _didMoveFromWindow:toWindow:] + 712
    11 UIKit                          0x18a95845c -[UIView(Internal) _didMoveFromWindow:toWindow:] + 712
    12 UIKit                          0x18a95845c -[UIView(Internal) _didMoveFromWindow:toWindow:] + 712
    13 UIKit                          0x18a95845c -[UIView(Internal) _didMoveFromWindow:toWindow:] + 712
    14 UIKit                          0x18a95845c -[UIView(Internal) _didMoveFromWindow:toWindow:] + 712
    15 UIKit                          0x18a95845c -[UIView(Internal) _didMoveFromWindow:toWindow:] + 712
    16 UIKit                          0x18a957918 __45-[UIView(Hierarchy) _postMovedFromSuperview:]_block_invoke + 156
    17 Foundation                     0x181e7c59c -[NSISEngine withBehaviors:performModifications:] + 168
    18 UIKit                          0x18a95778c -[UIView(Hierarchy) _postMovedFromSuperview:] + 824
    19 UIKit                          0x18a96339c -[UIView(Internal) _addSubview:positioned:relativeTo:] + 1728
    20 UIKit                          0x18abb3158 __53-[_UINavigationParallaxTransition animateTransition:]_block_invoke_2 + 1660
    21 UIKit                          0x18a969a84 +[UIView(Animation) performWithoutAnimation:] + 104
    22 UIKit                          0x18ab23864 __53-[_UINavigationParallaxTransition animateTransition:]_block_invoke + 264
    23 UIKit                          0x18ac418a4 +[UIView(Internal) _performBlockDelayingTriggeringResponderEvents:] + 220
    24 UIKit                          0x18ab2321c -[_UINavigationParallaxTransition animateTransition:] + 1112
    25 UIKit                          0x18aae1720 -[UINavigationController _startCustomTransition:] + 3444
    26 UIKit                          0x18aa02e04 -[UINavigationController _startDeferredTransitionIfNeeded:] + 712
    27 UIKit                          0x18aa02a34 -[UINavigationController __viewWillLayoutSubviews] + 124
    28 UIKit                          0x18aa0295c -[UILayoutContainerView layoutSubviews] + 188
    29 UIKit                          0x18a959000 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1256
    30 QuartzCore                     0x1855290b4 -[CALayer layoutSublayers] + 184
    31 QuartzCore                     0x18552d194 CA::Layer::layout_if_needed(CA::Transaction*) + 332
    32 QuartzCore                     0x18549bf24 CA::Context::commit_transaction(CA::Transaction*) + 336
    33 QuartzCore                     0x1854c2340 CA::Transaction::commit() + 540
    34 QuartzCore                     0x1854c3180 CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) + 92
    35 CoreFoundation                 0x1814f38b8 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 32
    36 CoreFoundation                 0x1814f1270 __CFRunLoopDoObservers + 412
    37 CoreFoundation                 0x1814f182c __CFRunLoopRun + 1292
    38 CoreFoundation                 0x1814122d8 CFRunLoopRunSpecific + 436
    39 GraphicsServices               0x1832a3f84 GSEventRunModal + 100
    40 UIKit                          0x18a9bf880 UIApplicationMain + 208

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