iOS从通知中删除观察者:我可以为所有观察者调用一次吗?即使没有?

时间:2014-01-28 22:56:54

标签: ios objective-c nsnotificationcenter

我在大多数视图控制器中注册了三个观察者。有些人有更多,有些人更少,但我想在父类中包含部分注册和注销过程。即使没有观察者,调用取消注册有什么问题吗?是否要求所有三位观察员取消注册?

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

2 个答案:

答案 0 :(得分:58)

是的,这将删除观察者为self的所有注册。它记录在NSNotificationCenter Class Reference

  

以下示例说明了如何为之前注册过的所有通知取消注册someObserver

[[NSNotificationCenter defaultCenter] removeObserver:someObserver];

请注意,理论上(但据我所知,在iOS 7.0的实践中),UIViewController可能有自己的注册,不希望在viewWillDisappear:中删除。使用addObserver:selector:name:object:注册公共API中的任何通知都不太可能,因为这会阻止您在UIViewController子类中注册它们,但它当然可以注册非公开通知未来的版本。

取消注册的一种安全方法是每次注册时发送一次removeObserver:name:object:

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

另一种方法是使用addObserverForName:object:queue:usingBlock:注册(而不是addObserver:selector:name:object:)。这将为每个注册返回一个新的观察者对象引用。您必须将它们保存起来(如果您不想创建单个实例变量,可能在NSArray实例变量中)。然后将每个传递给removeObserver:以取消注册其通知。例如:

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

由于addObserverForName:object:queue:usingBlock:返回的每个观察者都是一个只有一次注册的新对象,因此每次调用removeObserver:都只能删除该观察者的一次注册。

iOS 9 / macOS 10.11及更高版本的更新

从iOS 9和macOS 10.11开始,如果取消分配观察者,NSNotificationCenter会自动取消注册观察者。如果您的部署目标是iOS 9或更高版本或macOS 10.11或更高版本,则不再需要在dealloc方法(或Swift中的deinit)中手动取消注册。

答案 1 :(得分:7)

对于你的第一个问题,即使没有观察者也可以取消注册。 但是对于你移除观察者的方式,[[NSNotificationCenter defaultCenter] removeObserver:someObserver];甚至会删除超级观察者,这是非常不推荐的(除了dealloc因为对象已被卸载),但在viewWillDisappear中你应该删除观察员使用[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];

逐个观察