web-dev-qa-db-fra.com

KVO - Comment vérifier si un objet est un observateur?

Lorsque vous observez une valeur sur un objet à l'aide de addObserver:forKeyPath:options:context:, vous souhaiterez éventuellement appeler removeObserver:forKeyPath: sur cet objet pour le nettoyer ultérieurement. Avant de faire cela, est-il possible de vérifier si un objet observe réellement cette propriété?

J'ai essayé de m'assurer dans mon code qu'un objet est supprimé uniquement par un observateur, mais il est parfois possible que l'observateur puisse essayer de se supprimer deux fois. Je travaille pour empêcher cela, mais juste au cas où, je viens juste d'essayer de savoir s'il existe un moyen de vérifier d'abord si mon code est réellement un observateur de quelque chose.

38
Josh Buhler

[...] est-il possible de vérifier si un objet observe réellement cela propriété?

Non. Lorsque vous traitez avec KVO, vous devez toujours garder à l'esprit le modèle suivant:

Lorsque vous établissez une observation, vous êtes responsable de la suppression de cette observation exacte. Une observation est identifiée par son contexte - par conséquent, le contexte doit être unique. Lorsque vous recevez des notifications (et, dans Lion, lorsque vous supprimez l'observateur), vous devez toujours tester le contexte, pas le chemin.

La meilleure pratique pour manipuler les objets observés est de supprimer et d’établir l’observation dans le poseur de l’objet observé:

static int fooObservanceContext;

- (void)setFoo:(Foo *)foo
{
    [_foo removeObserver:self forKeyPath:@"bar" context:&fooObservanceContext];

    _foo = foo; // or whatever ownership handling is needed.

    [foo addObserver:self forKeyPath:@"bar" options:0 context:&fooObservanceContext];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if (context == &fooObservanceContext) {
        // handle change
    } else {
        // not my observer callback
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

- (void)dealloc
{
    self.foo = nil; // removes observer
}

Lorsque vous utilisez KVO, vous devez vous assurer que les deux objets, observateur et observateur, sont en vie tant que l'observation est en place.

Lorsque vous ajoutez une observation, vous devez équilibrer cela avec exactement une suppression de la même observation. Ne présumez pas que vous êtes le seul à utiliser KVO. Les classes de structure peuvent utiliser KVO à leurs propres fins. Par conséquent, vérifiez toujours le contexte dans le rappel.

Une dernière question que je voudrais signaler: la propriété observée doit être conforme à KVO. Vous ne pouvez rien observer .

44
Nikolai Ruhe

Une partie du protocole NSKeyValueObserving est la suivante:

 - (void *)observationInfo

qui devrait énumérer les observateurs.

MODIFIER Utile pour le débogage uniquement.

26
Rayfleck

Je sous-estime que c’est une question objectif-c. Mais comme beaucoup de gens utilisent Swift/objective-c ensemble, j'ai donc souligné l'avantage de la nouvelle API Swift4 par rapport aux anciennes versions de KVO:

Si vous modifiez addObserver plusieurs fois pour KVO, vous obtiendrez la observeValue pour chaque modification, autant que le nombre actuel de fois que vous vous êtes ajouté comme observateur.

  • Et pour vous retirer, vous devez appeler removeObserver autant de fois que vous avez ajouté.
  • Le supprimer plus que ce que vous avez ajouté entraînera un crash

La Swift4 observe est beaucoup plus intelligente et rapide!

  • Si vous le faites plusieurs fois, cela n’a pas d’importance. Il ne donnera pas plusieurs rappels pour chaque changement.
  • Et une seule invalidate de la token suffit.
  • invalidating avant de commencer à observer ou plusieurs fois que vous avez fait observe ne provoquera pas de plantage

Donc, pour répondre spécifiquement à votre question, si vous utilisez le nouveau KVO Swift4, vous n’avez pas besoin de vous en soucier. Il suffit d'appeler invalidate et vous êtes bon. Mais si vous utilisez l'ancienne API, reportez-vous à la réponse de Nikolai

0
Honey