Dans le nouvel iOS11, je reçois d'étranges exceptions. Je ne comprends pas pourquoi cela se produit. Dans le précédent iOS, il n'y avait pas une telle exception. Journal joint:
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
Qui a déjà rencontré ça? Qu'est-ce que c'est et comment le vaincre?
C'est lié à KVO = Key-Value Observing. Vérifiez si vous appelez la fonction
object.addObserver(self, forKeyPath:..., options:..., context:...)
quelque part; c'est votre observateur du KVO. Cette classe remplacerait également la fonction
observeValue(forKeyPath:of:change:context:)
Comme l'indique le message d'erreur, si vous obtenez un crash ici, cela signifie que l'observateur a été "libéré" ou "écrasé". Je pense que cela signifie simplement qu'il a été libéré tout en observant le chemin clé.
Si vous devez le corriger dans Swift 3 (comme je l'ai fait), assurez-vous d'appeler removeObserver
une fois que vous n'êtes plus intéressé par l'observation du chemin de clé. La façon la plus simple de cela consiste à ajouter une méthode deinit
à votre observateur:
deinit {
object.removeObserver(self, forKeyPath:#keyPath(same.as.in.addObserver))
}
Assurez-vous de remplacer object
et le chemin d'accès par les mêmes références que vous avez utilisées dans addObserver
!
Plus d'informations: https://cocoacasts.com/key-value-observing-kvo-and-Swift-3/
De Swift 4/iOS 11, vous pouvez utiliser des blocs, comme dans cette question: Dans Swift 4, comment puis-je supprimer un bloc basé sur Observateur du KVO?
Au lieu d'utiliser la méthode observeValue, vous pouvez simplement ajouter l'observateur comme ceci:
var observer: NSKeyValueObservation? = foo.observe(.value, options: [.new]) { (foo, change) in
print(change.newValue) // whatever needs to happen when the value changes
}
Dans iOS, vous devez toujours conserver une référence à l'observateur et appeler invalidate
dessus à un moment approprié, par exemple dans deinit
ou dans viewWillDisappear
.
Si vous développez pour macOS 10.13 ou une version plus récente, dans certaines conditions, il n'est plus nécessaire de les supprimer. Citation des documents:
Exigences de désinscription de l'observation des valeurs-clés assouplies
Avant la version 10.13, le KVO lançait une exception si des observateurs étaient encore enregistrés après la fin de l'exécution de -dealloc d'un objet autonotifiant. En outre, si tous les observateurs ont été supprimés, mais certains ont été supprimés d'un autre thread pendant dealloc, l'exception serait toujours incorrectement levée. Cette exigence a été assouplie en 10.13, sous réserve de deux conditions:
- L'objet doit utiliser la notification automatique KVO, plutôt que d'appeler manuellement -will et -didChangeValueForKey: (c'est-à-dire qu'il ne doit pas renvoyer NO de + automaticallyNotifiesObserversForKey :)
- L'objet ne doit pas remplacer les accesseurs (privés) pour l'état KVO interne
Si toutes ces informations sont vraies, tous les observateurs restants après les retours de -dealloc seront nettoyés par le KVO; c'est aussi un peu plus efficace que d'appeler plusieurs fois les méthodes -removeObserver.
Source: https://developer.Apple.com/library/archive/releasenotes/Foundation/RN-Foundation/index.html