在iOS 5 ARC中移除NSNotificationCenter观察者

21

我有一个基于iOS 5的ARC项目,在一个UIViewController中注册了NSNotificationCenter观察,但是不知道应该在哪里删除这个观察者。在stackoverflow上看到过一些类似的帖子,它们说应该在-dealloc方法中删除。虽然在ARC项目中这个方法并不是必需的,但我已经添加了以下代码:

- (void)dealloc {

    [[NSNotificationCenter defaultCenter] removeObserver:self];

}
作为测试,我打开一个 UIViewController(在 UINavigationController 中),执行一些会触发通知的操作,然后通过点击“返回”按钮将其弹出堆栈。然后重新打开 UIViewController,并执行一些更多的操作以触发通知,但是注意到每个回调都被调用两次 - 这表明之前的通知未被注销。重复这个过程只会导致每个回调被调用多次,因此它们似乎永远不会被注销。
任何帮助将不胜感激!

我也遇到了同样的问题,非常让人恼火。在经过数小时的调试之后,进行一次清理+构建操作“解决”了这个问题。在此期间,我一直在iPhone和模拟器上逐步执行dealloc和-removeObsever方法。因此,如果有其他人遇到了这个罕见的问题,请先重新构建。 - emp
你最初是怎么注册的?你需要展示更多的代码。 - matt
NSNotification中心是否会保留观察者? - Nick Weaver
1
事实上,在使用ARC时,dealloc永远不会被调用。这是按设计的。 - Johan Karlsson
3个回答

7
很明显,您的dealloc方法没有被调用(removeObserver也没有)。为什么不在viewDidUnload:viewWillDisappear:方法中删除UIViewController的观察者?

5
据我所知,viewDidUnload:只有在低内存条件下才会被调用。我还需要确保即使视图不在屏幕上,通知回调仍然会触发,这意味着我不能使用viewWillAppear:viewWillDisappear: - Skoota
1
顺便提一下,如果您正在从堆栈中弹出视图控制器,那么它应该被释放。如果没有,那么其他东西是否保留了您的视图控制器?请使用Instruments进行检查。哦,还有这里有一个相关的问题可能对您有帮助 - Michael Dautermann

7

如果您的dealloc方法没有被调用,很可能是因为有人仍然持有对视图控制器的引用。也许您需要将某些内容标记为__weak?您可以使用分配工具来帮助跟踪保留视图控制器的内容。


3

“如果视图不在屏幕上,我仍然需要接收通知回调。” -> 您可能需要注册UIApplicationWillEnterForegroundNotification。如果是这样,请尝试以下操作:

- (void)viewWillAppear:(BOOL)animated {
    NSLog(@"viewWillAppear");
    [super viewWillAppear:animated];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(applicationDidEnterBackground:)
                                                 name:UIApplicationDidEnterBackgroundNotification
                                               object:nil];
}

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

- (void)applicationWillEnterForeground:(UIApplication *)application {
    NSLog(@"applicationWillEnterForeground");
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(applicationDidEnterBackground:)
                                                 name:UIApplicationDidEnterBackgroundNotification
                                               object:nil];
    // do your stuff here
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    NSLog(@"applicationDidEnterBackground");
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(applicationWillEnterForeground:)
                                                 name:UIApplicationWillEnterForegroundNotification
                                               object:nil];
}

这个想法是在进入和退出屏幕时添加或删除UIApplicationDidEnterBackgroundNotification。当应用程序进入后台时,我们只注册UIApplicationWillEnterForegroundNotification,并在回到前台时将其删除。请注意,我们只会在viewWillDisappear时删除UIApplicationDidEnterBackgroundNotification。

我的dealloc()不知何故没有被调用,所以我找到了这种方法,希望对您也有用。

享受吧 :)


你忘记在那些地方调用 [super ...] 了,根据文档要求。 - GregJaskiewicz
它们是必需的吗?我在我的应用程序中使用这个代码,没有它们似乎也没有问题。 - thanhbinh84
7
当超级对象执行自己的操作时,你会遇到一个问题。养成这个习惯,因为文档上这么说。否则,某一天你可能会遇到奇怪的问题。 - GregJaskiewicz
1
谢谢Greg,你是正确的。他们在API文档中提到了http://developer.apple.com/library/ios/#documentation/uikit/reference/UIViewController_Class/Reference/Reference.html - thanhbinh84
1
@doraemon 如果你在这些方法中不调用super,迟早会遇到问题。实际上,它们正在执行一些“神奇的事情”。我见过许多问题都是通过添加对super的调用来解决的。 - Johan Karlsson
为什么不在 initviewDidLoad 中添加并删除这些通知,而不是一直添加和删除呢?或者如果你真的必须在视图消失时将它们删除,那就在 viewWillAppear 中添加它们,在 viewDidDisappear 中删除它们。没有必要瞎忙 :) - Chris Nolet

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