KVO et ARC comment supprimerObserver

87

Comment supprimer un observateur d'un objet sous ARC ? Ajoutons-nous simplement l'observateur et oublions-nous de le supprimer? Si nous ne gérons plus la mémoire manuellement, où démissionnerons-nous de l'observation?

Par exemple, sur un contrôleur de vue:

[self.view addObserver:self
            forKeyPath:@"self.frame"
               options:NSKeyValueObservingOptionNew 
               context:nil];

Auparavant, removeObserver:j'appelais la deallocméthode du contrôleur de vue .

drunknbass
la source
4
Notez que c'est une très mauvaise idée de KVO .frame. Comme écrit ailleurs par les ingénieurs Apple sur StackOverflow, la propriété frame d'UIKit n'est pas compatible KVO. Quand ça marche, ce n'est que par pur hasard.
steipete
2
Votre keyPath ne devrait-il pas être @"frame"plutôt que @"self.frame"?
Besi

Réponses:

126

Vous pouvez toujours implémenter -deallocsous ARC, qui semble être l'endroit approprié pour supprimer l'observation des valeurs clés. Vous n'appelez plus [super dealloc]depuis cette méthode.

Si vous étiez -releaseavant, vous faisiez les choses de la mauvaise façon.

Brad Larson
la source
1
Es-tu sûr de ça? Je cite clang.llvm.org/docs/… , section 7.1.2. dealloc: "Raison d'être: même si ARC détruit automatiquement les variables d'instance, il existe toujours des raisons légitimes d'écrire une méthode dealloc, comme la libération de ressources non conservables. Ne pas appeler [super dealloc] dans une telle méthode est presque toujours un bogue."
Elise van Looij
@ElisevanLooij Oui, c'est vrai. Si vous dérivez de cette classe, il semble évident que vous devez appeler [super dealloc]. Qui d'autre devrait faire cela pour vous.
Björn Landmesser
@ElisevanLooij Oups, eh bien, j'aurais dû vérifier avant. Il n'est pas permis d'appeler [super dealloc]une méthode dealloc. Aucune idée de comment cela fonctionnerait alors lors du sous-classement de la classe mentionnée. Peut-être qu'il est simplement conseillé d'utiliser à la finalizeplace (où vous appelez [super finalize])
Björn Landmesser
17
@ElisevanLooij - Le point qu'ils essayaient de faire ici concerne le cas de la gestion manuelle de la mémoire. Parce que ne pas appeler le [super dealloc]dernier dans cette méthode est presque toujours un bogue dans la gestion manuelle de la mémoire, le compilateur le gère pour vous maintenant, c'est pourquoi vous ne pouvez plus appeler -deallocdirectement. Les seules choses que vous mettez dans une -deallocméthode sous ARC sont toutes les ressources non-objet dont vous avez besoin pour libérer ou nettoyer des tâches telles que la suppression d'observateurs. Le libellé qu'ils utilisent est un peu confus, mais c'est ce qu'ils voulaient dire.
Brad Larson
7
@ BjörnMilcke - Comme je commente la réponse d'Elise, -finalizeest utilisé pour cela sous garbage collection, où -deallocn'est jamais appelé, mais il est parfaitement acceptable de placer ce code -deallocsous ARC. [super dealloc]est appelé automatiquement pour vous, c'est pourquoi c'est une erreur de l'appeler sous ARC.
Brad Larson
1

Je le fais avec ce code

- (void)dealloc
{
@try{
    [self.uAvatarImage removeObserver:self forKeyPath:@"image" context:nil];
} @catch(id anException) {
    //do nothing, obviously it wasn't attached because an exception was thrown
}
}    
user3461902
la source
2
Quel est l'intérêt de la gestion des exceptions dealloc? Il est trop tard pour faire quoi que ce soit.
Abizern
Quel est l'intérêt de supprimer des observateurs sur une variable d'instance dans dealloc? Cette uAvatarImage sera bientôt désallouée avec tous les observateurs auxquels elle a souscrit ses chemins clés.
shoumikhin
1
@shoumikhin J'utilise ARC et j'ai dû supprimer l'observateur dans la méthode dealloc. J'ai la même question que vous. Cependant, lorsque j'ai exécuté plusieurs instances de la classe, j'ai finalement eu l'erreur exc_bad_address. Cela a résolu le problème. En outre, la réponse d'ici stackoverflow.com/questions/32490808/ ... m'a aidé à découvrir le problème.
mac10688
-2

Ailleurs sur le débordement de pile, Chris Hanson conseille d'utiliser la méthode finalize à cet effet et d'implémenter une méthode invalider distincte afin que les propriétaires puissent dire aux objets qu'ils ont terminé. Dans le passé, j'ai trouvé que les solutions de Hanson étaient bien pensées, alors je vais y aller.

Elise van Looij
la source
13
Notez qu'il faisait référence à la collecte des ordures là-bas, pas à ARC (sa réponse a été écrite en 2008). Sous garbage collection, -deallocn'est jamais appelé. Dans ARC, c'est le cas. Il est parfaitement acceptable de supprimer les observateurs du KVO dans -dealloc, comme Chris Lattner (qui sait de quoi il parle) l'indique dans les forums de développeurs d'Apple ici: devforums.apple.com/message/475850
Brad Larson
3
Merci Brad, pour tout ce travail. Non pour finaliser, oui pour dealloc mais sans [super dealloc]. Simple vraiment, une fois que vous le savez. Hé, @drunknbass, accepte la réponse de cet homme!
Elise van Looij