web-dev-qa-db-ja.com

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

誰がこれに遭遇しましたか?それは何であり、それを打ち負かす方法は?

18
Mikhail S

KVO = Key-Value Observingに関連しています。関数を呼び出しているかどうかを確認します

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))
}

objectとキーパスを、addObserverで使用したのと同じ参照に置き換えてください。

詳細: https://cocoacasts.com/key-value-observing-kvo-and-Swift-3/

Swift 4/iOS

Swift 4/iOS 11では、この質問のようにブロックを使用できます: In 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を呼び出す必要があります。 deinitまたはviewWillDisappearで。

Swift 4/macOS

MacOS 10.13以降向けに開発している場合、特定の条件下ではそれらを削除する必要はありません。ドキュメントからの引用:

登録解除要件を遵守する緩和されたキー値

10.13より前は、自動通知オブジェクトの-deallocの実行が終了した後、オブザーバーがまだ登録されている場合、KVOは例外をスローしました。さらに、すべてのオブザーバーが削除されたが、deallocの間に別のスレッドから一部が削除された場合、例外は依然として誤ってスローされました。この要件は10.13で緩和され、2つの条件があります。

  • オブジェクトは、-willおよび-didChangeValueForKeyを手動で呼び出すのではなく、KVO自動通知を使用する必要があります(つまり、+ automaticallyNotifiesObserversForKey:からNOを返すべきではありません)
  • オブジェクトは、内部KVO状態の(プライベート)アクセサーをオーバーライドしてはなりません

これらすべてが真である場合、-deallocが戻った後に残っているオブザーバーはKVOによってクリーンアップされます。これは、-removeObserverメソッドを繰り返し呼び出すよりも効率的です。

出典: https://developer.Apple.com/library/archive/releasenotes/Foundation/RN-Foundation/index.html

17
Alex Wally