web-dev-qa-db-fra.com

iOS Supprimer l'observateur de la notification: puis-je appeler une fois pour tous les observateurs? Et même s'il n'y en a pas?

J'enregistre trois observateurs dans la plupart de mes contrôleurs de vue. Certains ont plus, d'autres moins, mais je veux inclure une partie du processus d'inscription et de désinscription dans une classe parent. Y a-t-il un problème à appeler l'annulation de l'enregistrement même s'il n'y a pas d'observateur? Et un appel à se désinscrire suffit-il pour les trois observateurs?

- (void)registerForKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWasShown:)
                                                 name:UIKeyboardWillShowNotification object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillBeHidden:)
                                                 name:UIKeyboardWillHideNotification object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(applicationWillEnterBackground:)
                                                 name:UIApplicationWillResignActiveNotification
                                               object:nil];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];

    //Has to be unregistered always, otherwise nav controllers down the line will call this method
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
28
MichiZH

Oui, cela supprimera toutes les inscriptions où l'observateur est self. Il est documenté dans le NSNotificationCenter Class Reference :

L'exemple suivant montre comment désinscrire someObserver pour toutes les notifications pour lesquelles il s'était précédemment enregistré:

[[NSNotificationCenter defaultCenter] removeObserver:someObserver];

Notez qu'en théorie (mais pas, autant que je sache, en pratique depuis iOS 7.0), UIViewController pourrait avoir ses propres enregistrements qu'il ne souhaite pas supprimer dans viewWillDisappear:. Il est peu probable de s'inscrire à l'une des notifications de l'API publique à l'aide de addObserver:selector:name:object:, car cela vous empêcherait de vous inscrire dans votre sous-classe UIViewController, mais il pourrait certainement s'inscrire pour les notifications non publiques maintenant ou dans une version future.

Un moyen sûr de se désinscrire consiste à envoyer removeObserver:name:object: une fois pour chaque inscription:

- (void)deregisterForKeyboardNotifications {
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    [center removeObserver:self name:UIKeyboardWillShowNotification object:nil];
    [center removeObserver:self name:UIKeyboardWillHideNotification object:nil];
    [center removeObserver:self name:UIApplicationWillResignActiveNotification object:nil];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    [self deregisterForKeyboardNotifications];
}

- (void)dealloc {
    [self deregisterForKeyboardNotifications];
}

Une autre façon consiste à utiliser addObserverForName:object:queue:usingBlock: pour vous inscrire (au lieu de addObserver:selector:name:object:). Cela renvoie une nouvelle référence d'objet observateur pour chaque enregistrement. Vous devez les sauvegarder (peut-être dans une variable d'instance NSArray si vous ne voulez pas créer de variables d'instance individuelles). Ensuite, vous passez chacun à removeObserver: pour désenregistrer sa notification. Exemple:

@implementation MyViewController {
    NSMutableArray *observers;
}

- (void)registerForKeyboardNotifications {
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    NSOperationQueue *queue = [NSOperationQueue mainQueue];
    __weak MyViewController *me = self;
    observers = [NSMutableArray array];
    [observers addObject:[center addObserverForName:UIKeyboardWillShowNotification
        object:nil queue:queue usingBlock:^(NSNotification *note) {
            [me keyboardWillShow:note];
        }]];
    [observers addObject:[center addObserverForName:UIKeyboardWillHideNotification
        object:nil queue:queue usingBlock:^(NSNotification *note) {
            [me keyboardWillHide:note];
        }]];
    [observers addObject:[center addObserverForName:UIApplicationWillResignActiveNotification
        object:nil queue:queue usingBlock:^(NSNotification *note) {
            [me applicationWillResignActive:note];
        }]];
}

- (void)deregisterForKeyboardNotifications {
    for (id observer in observers) {
        [[NSNotificationCenter defaultCenter] removeObserver:observer];
    }
    observers = nil;
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    [self deregisterForKeyboardNotifications];
}

- (void)dealloc {
    [self deregisterForKeyboardNotifications];
}

Puisque chaque observateur est revenu par addObserverForName:object:queue:usingBlock: est un nouvel objet qui n'a qu'un seul enregistrement, chaque appel à removeObserver: est garanti de ne supprimer que l'enregistrement unique de cet observateur.

Mise à jour pour iOS 9/macOS 10.11 et versions ultérieures

Depuis iOS 9 et macOS 10.11, NSNotificationCenter annule automatiquement l'enregistrement d'un observateur s'il est désalloué. Il n'est plus nécessaire de vous désinscrire manuellement dans votre méthode dealloc (ou deinit dans Swift) si votre cible de déploiement est iOS 9 ou version ultérieure ou macOS 10.11 ou version ultérieure.

58
rob mayoff

Pour votre première Question, le désenregistrement même s'il n'y a pas d'observateur est OK. Mais pour la façon dont vous supprimez l'observateur, [[NSNotificationCenter defaultCenter] removeObserver:someObserver]; supprimera même les observateurs de super classe, ce qui est fortement déconseillé (sauf dans dealloc car l'objet est déchargé) mais dans viewWillDisappear vous devez supprimer les observateurs un par un en utilisant [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];

7
MAB