web-dev-qa-db-fra.com

iOS 11. Que signifie KVO_IS_RETAINING_ALL_OBSERVERS_OF_THIS_OBJECT_IF_IT_CRASHES_AN_OBSERVER_WAS_OVERRELEASED_OR_SMASHED?

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?

18
Mikhail S

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é.

Comment le réparer?

Swift 3

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/

Swift 4/iOS

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.

Swift 4/macOS

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

17
Alex Wally