使用 NSNotification 的 removeObserver...我做错了什么?

28

我有一个视图view1,它在某个时候通过presentModalViewController:animated:调用视图view2。当view2中的某个UIButton被按下时,view2会调用view1中的一个通知方法并立即关闭。该通知方法会弹出一个警报。

通知方法可以正常工作并且可以适当地被调用。问题是,每次创建view1(同一时间只能存在一个view1),我可能会得到另一个NSNotification的创建,因为如果我从view0(菜单)转到view1,然后来回几次,我会得到一系列相同的警报消息,从通知方法中打开与我打开view1的次数一样多的警报。

以下是我的代码,请告诉我我做错了什么:

View1.m

-(void) viewDidLoad {
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(showAlert:) 
                                                 name:@"alert" 
                                               object:nil];
}

-(void) showAlert:(NSNotification*)notification {
    // (I've also tried to swap the removeObserver method from dealloc
    // to here, but it still fails to remove the observer.)
    // < UIAlertView code to pop up a message here. >
}

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

View2.m

-(IBAction) buttonWasTapped {
    [[NSNotificationCenter defaultCenter] postNotificationName:@"alert" 
                                                        object:nil];
    [self dismissModalViewControllerAnimated:YES];
}

-(void) dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [super dealloc];
}
3个回答

61

在视图控制器被解散之后,调用-dealloc并不会自动发生——视图控制器的生命周期中仍然可能存在一些“生命”。在这段时间内,该视图控制器仍然订阅该通知。

如果你在-viewWillDisappear:或者-viewDidDisappear:中移除观察者,这将会更加立即地生效:

- (void) viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    [[NSNotificationCenter defaultCenter] removeObserver:self 
                                                    name:@"alert" 
                                                  object:nil];
}

这可能是因为当我在同一个视图中使用presentModalViewController时,viewWillDisappear被调用了吗?我不确定问题出在哪里。 - Derek
这个方法是从 UIView 还是 UIViewController 实例中调用的? - Alex Reynolds
-viewWillDisappear: 是在 UIViewController 子类中还是在 UIView 子类中调用的?你必须只从 UIViewController 中重写 -viewWillDisappear:。同时,确保添加你要移除观察的通知的 nameobject,这是你问题中的代码没有包括的部分(这可能解释了为什么它没有起作用)。 - Alex Reynolds
啊...是的,view1(注册通知并包含通知方法的视图)继承自UIViewController。我还更改了removeObserver:name:object:方法,并且没有收到警告或错误,但仍然在viewWillDisappear:方法中放置该方法调用会阻止通知方法被调用。只有当我注释掉它时,通知方法才会执行。我在voidDidLoad中注册通知,这可能会引起问题吗?老实说,我很困惑! - Derek
我不完全理解你的视图控制器是如何被推送的。也许可以在 -viewWillAppear: 中注册通知观察者,在 -viewWillDisappear: 中取消注册。 - Alex Reynolds
显示剩余3条评论

6
如果您在viewWillDisappear:viewDidDisappear:中实现了Observer的删除,则不应该在viewDidLoad中保留添加观察者。相反,将添加观察者放在viewWillAppear:中。您遇到的问题是因为当任何视图显示在UIViewController视图之上时,会发生您的观察者的删除,而您在viewDidLoad中添加了观察者,这只会发生一次,因此它将丢失。请记住,这种方法适用于您不希望在主视图不在前台时观察的对象。
另外,请注意viewDidUnload也已被弃用。

2

removeObserver:放在dealloc中没有任何问题。只是因为它没有被调用,意味着在dismiss后view1没有被正确释放。看起来有些东西持有了指向你的view1的指针,请检查是否存在循环引用。

另外,不应该在super上调用dealloc。


仅在ARC模式下不要调用super的dealloc方法。 - Rickster

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接