新しい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に関連しています。関数を呼び出しているかどうかを確認します
object.addObserver(self, forKeyPath:..., options:..., context:...)
どこか;それがあなたのKVOオブザーバーです。このクラスは関数もオーバーライドします
observeValue(forKeyPath:of:change:context:)
エラーメッセージにあるように、ここでクラッシュが発生した場合、それはオブザーバーが「過剰リリース」または「破壊」されたことを意味します。キーパスを観察しながらリリースされただけだと思います。
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 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
で。
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