iOS 11. KVO_IS_RETAINING_ALL_OBSERVERS_OF_THIS_OBJECT_IF_IT_CRASHES_AN_OBSERVER_WAS_OVERRELEASED_OR_SMASHED是什么意思?

在新的iOS11中,我得到了一些奇怪的例外。 我不明白为什么会这样。 在之前的iOS中,没有这样的例外。 附加日志:

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 

曾经遇到过这个? 它是什么以及如何击败它?

它与KVO = Key-Value Observing有关。 检查您是否正在调用该function

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

某处; 这是你的KVO观察员。 该类还将覆盖该函数

 observeValue(forKeyPath:of:change:context:) 

正如错误消息所说,如果你在这里遇到崩溃,那就意味着观察者被“过度释放”或“被粉碎”。 我认为这只是意味着它仍在观察关键路径时被释放。

怎么解决?

斯威夫特3

如果您需要在Swift 3中修复它(就像我一样),请确保在您不再对观察密钥路径感兴趣时调用removeObserver 。 最简单的方法是向观察者添加一个deinit方法:

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

确保使用addObserver使用的相同引用替换object和键路径!

更多信息: https : //cocoacasts.com/key-value-observing-kvo-and-swift-3/

Swift 4 / iOS

从Swift 4 / iOS 11中你可以使用块,就像这个问题一样: 在Swift 4中,如何删除基于块的KVO观察者?

您可以像这样添加观察者,而不是使用observeValue方法:

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

在iOS中,您仍应保留对观察者的引用,并在适当的时间调用invalidate ,例如在deinitviewWillDisappear

Swift 4 / macOS

如果您正在为macOS 10.13或更高版本开发,在某些情况下它们不再需要被删除。 从文档引用:

轻松的键值观察注销要求

在10.13之前,如果在autonotifying对象的-dealloc完成运行后仍然注册了任何观察者,KVO将抛出exception。 此外,如果删除了所有观察者,但是在dealloc期间某些观察者从另一个线程中删除了,则exception将被错误地抛出。 这一要求已在10.13中放宽,但有两个条件:

  • 该对象必须使用KVO自动调整,而不是手动调用-will和-didChangeValueForKey :(即它不应该从+自动返回NONotifiesObserversForKey 🙂
  • 对象不得覆盖内部KVO状态的(私有)访问器

如果所有这些都是真的,那么在-dealloc返回之后的任何剩余观察者将被KVO清理; 这比重复调用-removeObserver方法更有效。

资料来源: https : //developer.apple.com/library/archive/releasenotes/Foundation/RN-Foundation/index.html