web-dev-qa-db-fra.com

Comment savoir si un objet a un observateur de valeur clé attaché

si vous indiquez à un objet Objective C de retirer removeObservers: pour un chemin de clé et si ce chemin n'a pas été enregistré, les résultats sont corrigés. comme -

'Impossible de supprimer un observateur du chemin d'accès à la clé "theKeyPath", car il n'est pas enregistré en tant qu'observateur.'

y at-il un moyen de déterminer si un objet a un observateur enregistré, donc je peux le faire

if (object has observer){
  remove observer
}
else{
  go on my merry way
}
141
Aran Mulholland

Mettez un essai sur votre appel removeObserver

@try{
   [someObject removeObserver:someObserver forKeyPath:somePath];
}@catch(id anException){
   //do nothing, obviously it wasn't attached because an exception was thrown
}
310
Adam

La vraie question est pourquoi vous ne savez pas si vous l'observez ou non.

Si vous faites cela dans la classe de l'objet observé, arrêtez. Quoi qu'il observe, il s'attend à continuer à l'observer. Si vous coupez les notifications de l'observateur à son insu, attendez-vous à ce que les choses se cassent; plus spécifiquement, attendez-vous à ce que l'état de l'observateur devienne obsolète, car il ne reçoit pas de mises à jour de l'objet précédemment observé.

Si vous faites cela dans la classe de l'objet à observer, rappelez-vous simplement des objets que vous observez (ou, si vous observez un seul objet, que vous l'observiez). Cela suppose que l'observation est dynamique et entre deux objets par ailleurs non apparentés; si l'observateur possède l'observé, ajoutez-le simplement après avoir créé ou conservé l'observé et supprimez-le avant de relâcher l'observé.

L'ajout et la suppression d'un objet en tant qu'observateur doivent généralement se faire dans la classe de l'observateur, et jamais dans l'objet observé.

37
Peter Hosey

FWIW, [someObject observationInfo] semble être nil si someObject n’a aucun observateur. Cependant, je ne ferais pas confiance à ce comportement car je ne l'ai pas vu documenté. De plus, je ne sais pas comment lire observationInfo pour obtenir des observateurs spécifiques.

25
ma11hew28

La seule façon de procéder consiste à définir un indicateur lorsque vous ajoutez un observateur.

4
Leibowitzn

Lorsque vous ajoutez un observateur à un objet, vous pouvez l'ajouter à un NSMutableArray comme ceci:

- (void)addObservedObject:(id)object {
    if (![_observedObjects containsObject:object]) {
        [_observedObjects addObject:object];
    }
}

Si vous souhaitez ne pas observer les objets, vous pouvez faire quelque chose comme:

for (id object in _observedObjects) {
    if ([object isKindOfClass:[MyClass class]]) {
        MyClass *myObject = (MyClass *)object;
        [self unobserveMethod:myObject];
    }
}
[_observedObjects removeAllObjects];

rappelez-vous, si vous ne regardez pas un seul objet, supprimez-le de la _observedObjects tableau:

- (void)removeObservedObject:(id)object {
    if ([_observedObjects containsObject:object]) {
        [_observedObjects removeObject:object];
    }
}
4
Oritm

[someObject observationInfo] return nil s'il n'y a pas d'observateur.

if ([tableMessage observationInfo] == nil)
{
   NSLog(@"add your observer");
}
else
{
  NSLog(@"remove your observer");

}
3
Anupama

À mon avis, cela fonctionne de manière similaire au mécanisme retenue. Vous ne pouvez pas être sûr qu'au moment actuel vous avez votre observateur. Même si vous vérifiez: self.observationInfo - vous ne pouvez pas savoir avec certitude que vous aurez/n'aura plus d'observateurs à l'avenir.

Comme retentionCount. Peut-être que la méthode observationInfo n'est pas vraiment inutile, mais je ne l'utilise que pour le débogage.

En conséquence, vous devez le faire comme dans la gestion de la mémoire. Si vous avez ajouté un observateur, supprimez-le lorsque vous n'en avez pas besoin. J'aime utiliser les méthodes viewWillAppear/viewWillDisappear etc. Par exemple:

-(void) viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self addObserver:nil forKeyPath:@"" options:NSKeyValueObservingOptionNew context:nil];
}

-(void) viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    [self removeObserver:nil forKeyPath:@""];
}

Et si vous avez besoin de vérifications spécifiques, implémentez votre propre classe qui gère un éventail d’observateurs et utilisez-la pour vos vérifications.

3
quarezz

Le motif de l'observateur est de permettre à une classe observée d'être "scellée" - de ne pas savoir ou de ne pas se soucier de savoir si elle est observée. Vous essayez explicitement de briser ce modèle.

Pourquoi?

Le problème que vous rencontrez est que vous supposez que vous êtes observé alors que vous ne l'êtes pas. Cet objet n'a pas commencé l'observation. Si vous souhaitez que votre classe ait le contrôle de ce processus, vous devriez envisager d'utiliser le centre de notification. De cette façon, votre classe a un contrôle total sur le moment où les données peuvent être observées. Par conséquent, peu importe qui regarde.

2
adonoho

Je ne suis pas un fan de cette solution catch catch, donc ce que je fais la plupart du temps, c'est que je crée une méthode subscribe et unsubscribe pour une notification spécifique à l'intérieur de cette classe. Par exemple, ces deux méthodes souscrivent ou désabonnent l'objet à la notification globale au clavier:

@interface ObjectA : NSObject
-(void)subscribeToKeyboardNotifications;
-(void)unsubscribeToKeyboardNotifications;
@end

Dans ces méthodes, j'utilise une propriété privée qui a la valeur true ou false en fonction de l'état de l'abonnement, de la manière suivante:

@interface ObjectA()
@property (nonatomic,assign) BOOL subscribedToKeyboardNotification
@end

@implementation

-(void)subscribeToKeyboardNotifications {
    if (!self.subscribedToKeyboardNotification) {
        [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(onKeyboardShow:) name:UIKeyboardWillShowNotification object:nil];
        [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(onKeyboardHide:) name:UIKeyboardWillHideNotification object:nil];
        self.subscribedToKeyboardNotification = YES;
    }
}

-(void)unsubscribeToKeyboardNotifications {
    if (self.subscribedToKeyboardNotification) {
        [[NSNotificationCenter defaultCenter]removeObserver:self name:UIKeyboardWillShowNotification object:nil];
        [[NSNotificationCenter defaultCenter]removeObserver:self name:UIKeyboardWillHideNotification object:nil];
        self.subscribedToKeyboardNotification = NO;
    }
}
@end
1
Sebastian Boldt

En plus de la réponse d'Adam, je voudrais suggérer d'utiliser une macro comme celle-ci.

#define SafeRemoveObserver(sender, observer, keyPath) \
@try{\
   [sender removeObserver:observer forKeyPath:keyPath];\
}@catch(id anException){\
}

exemple d'utilisation

- (void)dealloc {
    SafeRemoveObserver(someObject, self, somePath);
}
0
wattson