web-dev-qa-db-fra.com

Pourquoi ma NSNotification son observateur est-il appelé plusieurs fois?

Dans une application, j'utilise plusieurs viewcontrollers. Sur un viewcontroller, un observateur est initialisé comme suit:

[[NSNotificationCenter defaultCenter] removeObserver:self name:@"MyNotification" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(myMethod:) name:@"MyNotification" object:nil];

Même lors de la suppression de NSNotification avant d'initialiser le nombre d'exécutions de myMethod: est résumée par le nombre de vues répétées sur le contrôleur de vue respectif.

Pourquoi cela se produit-il et comment puis-je éviter que myMethod soit appelé plus d'une fois.

Remarque: Je me suis assuré en utilisant des points d'arrêt que je n'ai pas fait d'erreur en appelant postNotification plusieurs fois.

Modifier: voici à quoi ressemble ma post-notification

NSArray * objects = [NSArray arrayWithObjects:[NSNumber numberWithInt:number],someText, nil];
NSArray * keys = [NSArray arrayWithObjects:@"Number",@"Text", nil];
NSDictionary * userInfo = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
[[NSNotificationCenter defaultCenter] postNotificationName:@"myNotification" object:self userInfo:userInfo];

edit: même après avoir déplacé mon abonnement à viewwillappear: j'obtiens le même résultat. myMethod: est appelé plusieurs fois. (nombre de fois que je recharge le viewcontroller).

-(void)viewWillAppear:(BOOL)animated {

    [super viewWillAppear:animated];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"MyNotification" object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(myMethod:) name:@"MyNotification" object:nil];
}

edit: quelque chose semble mal avec mon cycle de vie. ViewDidUnload et dealloc ne sont pas appelés, mais viewdiddisappear est appelé.

La façon dont je pousse mon Viewcontroller vers la pile est la suivante où le parent est une sous-classe de tableview (en cliquant sur la ligne, ce viewcontroller est lancé:

detailScreen * screen = [[detailScreen alloc] initWithContentID:ID andFullContentArray:fullContentIndex andParent:parent];
[self.navigationController pushViewController:screen animated:YES];

Solution:

Déplacer la suppression de nsnotification vers viewdiddisappear a fait l'affaire. Merci pour vos conseils!

25
BarryK88

Sur la base de cette description, une cause probable est que vos contrôleurs de vue sont trop conservés et ne sont pas libérés lorsque vous pensez qu'ils le sont. C'est assez courant même avec ARC si les choses sont trop retenues. Donc, vous pensez que vous n'avez qu'une seule instance d'un viewcontroller donné active, alors que vous avez en fait plusieurs instances en direct, et ils écoutent tous les notifications.

Si j'étais dans cette situation, je mettrais un point d'arrêt dans la méthode de désaffectation de viewcontroller et je m'assurerais qu'elle est désallouée correctement, si c'est la conception prévue de votre application.

37
Jaanus

Dans quelles méthodes avez-vous enregistré les observateurs?

Apple recommande que les observateurs soient enregistrés dans viewWillAppear: et non enregistré dans viewWillDissapear:

Êtes-vous sûr de ne pas enregistrer l'observateur deux fois?

37
Igor

Ran dans ce problème dans une application exécutant Swift. L'application a reçu la notification une fois lors de son premier lancement. la notification augmente le nombre de fois que vous passez en arrière-plan et revenez. c'est à dire

  • l'application en lance une - ajouter l'observateur est appelé une fois dans la vue apparaîtra ou la vue s'est chargée - la notification est appelée une fois
  • l'application passe en arrière-plan et revient, l'ajout d'un observateur est rappelé dans la vue apparaîtra ou la vue s'est chargée. la notification est appelée deux fois.
  • le nombre augmente le nombre de fois que vous passez en arrière-plan et revenez.
  • le code en vue disparaîtra ne fera aucune différence car la vue est toujours dans la pile de fenêtres et n'en a pas été supprimée.

solution: observez l'application va démissionner active dans votre contrôleur de vue:

  NSNotificationCenter.defaultCenter().addObserver(self, selector: "applicationWillResign:", name: UIApplicationWillResignActiveNotification, object: nil)

  func applicationWillResign(notification : NSNotification) {
    NSNotificationCenter.defaultCenter().removeObserver(self)
  }

cela garantira que votre contrôleur de vue supprimera l'observateur de la notification lorsque la vue passera en arrière-plan.

4
abhi

il est fort possible que vous vous abonniez aux notifications

[[NSNotificationCenter defaultCenter] postNotificationName:@"myNotification" object:self userInfo:userInfo];

avant auto est initialisé. Et en essayant de vous désinscrire 'self' qui n'est pas vraiment abonné, et vous obtiendrez toutes les notifications globales myNotification .

Si votre vue était connectée à IB, utilisez - awakeFromNib: comme point de départ pour vous inscrire aux notifications

1

Il est possible que la classe avec l'observateur soit, à juste titre, instanciée plusieurs fois. Lorsque vous déboguez, il semblera que la notification est publiée plusieurs fois. Mais si vous inspectez self, vous pouvez voir que chaque fois c'est pour une instance différente.

Cela pourrait facilement être le cas si votre application utilise une barre d'onglets et que l'observateur se trouve dans une classe de base dont vos contrôleurs de vue sont des sous-classes.

0
Murray Sagal