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];
}
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.
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.
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];