目标C:在哪里删除NSNotification的观察者?

时间:2011-06-24 14:26:05

标签: objective-c ios nsnotifications

我有一个客观的C类。在其中,我创建了一个init方法并在其中设置了NSNotification

//Set up NSNotification
[[NSNotificationCenter defaultCenter] addObserver:self 
                                         selector:@selector(getData)
                                             name:@"Answer Submitted"
                                           object:nil];

我在哪个地方设置[[NSNotificationCenter defaultCenter] removeObserver:self]?我知道对于UIViewController,我可以将其添加到viewDidUnload方法中如果我刚刚创建了一个目标c类,需要做什么?

14 个答案:

答案 0 :(得分:111)

通用答案将是“一旦您不再需要通知”。这显然不是一个令人满意的答案。

我建议您在这些类的方法[notificationCenter removeObserver: self]中添加一个调用dealloc,您打算将其用作观察者,因为这是彻底取消注册观察者的最后机会。但是,这只会保护您免受由于通知中心通知死对象而导致的崩溃。当您的对象尚未/不再处于可以正确处理通知的状态时,它无法保护您的代码免受接收通知。为此...见上文。

编辑(因为答案似乎吸引了比我想象的更多的评论)所有我想在这里说的是:很难就什么时候最好删除来自通知中心的观察员,因为这取决于:

  • 关于您的用例(观察到哪些通知?什么时候发送?)
  • 观察者的实施(何时准备接收通知?什么时候不再准备好?)
  • 观察者的预期生命时间(是否与某个其他对象相关联,例如视图或视图控制器?)
  • ...

所以,我可以提出最好的一般建议:保护你的应用程序。至少在一次可能的失败中,removeObserver:dealloc跳舞,因为那是最后一点(在对象的生命中),你可以干净利落地做到这一点。这并不意味着:“只要推迟删除,直到调用dealloc,一切都会好的”。相反,只要对象不再准备好(或需要)接收通知,就立即删除观察者。那是恰到好处的时刻。不幸的是,不知道上面提到的任何问题的答案,我甚至无法猜测,那个时刻会是什么时候。

您可以多次安全removeObserver:一个对象(除了与给定观察者的第一次调用之外的所有对象都是nops)。所以:考虑在dealloc中再次这样做(只是为了确定),但首要的是:在适当的时候(由你的用例决定)。

答案 1 :(得分:38)

注意:这已经过测试并且工作率为100%

夫特

override func viewWillDisappear(animated: Bool){
    super.viewWillDisappear(animated)

    if self.navigationController!.viewControllers.contains(self) == false  //any other hierarchy compare if it contains self or not
    {
        // the view has been removed from the navigation stack or hierarchy, back is probably the cause
        // this will be slow with a large stack however.

        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
}

PresentedViewController

override func viewWillDisappear(animated: Bool){
    super.viewWillDisappear(animated)

    if self.isBeingDismissed()  //presented view controller
    {
        // remove observer here
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
}

目标C

iOS 6.0 > version 中,最好将 viewWillDisappear 中的观察者移除为 viewDidUnload 方法弃用。

 [[NSNotificationCenter defaultCenter] removeObserver:observerObjectHere];

remove observer移除视图后,navigation stack or hierarchy会有好几倍。

- (void)viewWillDisappear:(BOOL)animated{
 if (![[self.navigationController viewControllers] containsObject: self]) //any other hierarchy compare if it contains self or not
    {
        // the view has been removed from the navigation stack or hierarchy, back is probably the cause
        // this will be slow with a large stack however.

        [[NSNotificationCenter defaultCenter] removeObserver:observerObjectHere];
    }
}

PresentedViewController

- (void)viewWillDisappear:(BOOL)animated{
    if ([self isBeingDismissed] == YES) ///presented view controller
    {
        // remove observer here
        [[NSNotificationCenter defaultCenter] removeObserver:observerObjectHere];
    }
}

答案 2 :(得分:37)

从iOS 9开始,不再需要移除观察者。

  

在OS X 10.11和iOS 9.0 NSNotificationCenter和   NSDistributedNotificationCenter将不再向其发送通知   可能被解除分配的注册观察员。

https://developer.apple.com/library/mac/releasenotes/Foundation/RN-Foundation/index.html#10_11NotificationCenter

答案 3 :(得分:25)

如果将观察者添加到视图控制器,我强烈建议您将其添加到viewWillAppear并在viewWillDisappear中将其删除。

答案 4 :(得分:18)

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

答案 5 :(得分:8)

一般情况下,我将其放入dealloc方法。

答案 6 :(得分:7)

快速使用deinit因为dealloc不可用:

deinit {
    ...
}

Swift文档:

  

在类实例之前立即调用deinitializer   释放。您使用deinit关键字编写deinitializers,类似   如何使用init关键字编写初始化器。 Deinitializers   仅适用于班级类型。

     

通常,您不需要执行手动清理   实例被解除分配。但是,当您使用自己的时   资源,您可能需要执行一些额外的清理   你自己。例如,如果您创建一个自定义类来打开文件和   向它写一些数据,你可能需要先关闭文件   类实例被解除分配。

答案 7 :(得分:5)

*编辑:此建议适用于iOS< = 5(即使您应该添加viewWillAppear并删除viewWillDisappear - 但是如果由于某种原因您添加了观察者在viewDidLoad

如果您已在viewDidLoad中添加了观察者,则应在deallocviewDidUnload中将其删除。否则,在viewDidLoad之后调用viewDidUnload时,最终会将其添加两次(这将在内存警告之后发生)。这在iOS 6中不是必需的,其中viewDidUnload已被弃用且不会被调用(因为视图不再自动卸载)。

答案 8 :(得分:5)

在我看来,以下代码在 ARC 中是没有意义的:

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

iOS 6 中,删除viewDidUnload中的观察者也没有意义,因为它现在已被弃用。

总而言之,我总是在viewDidDisappear中这样做。但是,这也取决于你的要求,就像@Dirk说的那样。

答案 9 :(得分:4)

我想我找到了一个可靠答案!我必须这样做,因为上面的答案含糊不清,看起来很矛盾。我浏览了Cookbook和Programming Guides。

首先,addObserver:中的viewWillAppear:removeObserver:中的viewWillDisappear:的样式对我不起作用(我测试了它),因为我在儿童中发布通知查看控制器以在父视图控制器中执行代码。如果我在同一个视图控制器中发布和监听通知,我只会使用这种风格。

我将最依赖的答案,我在iOS编程中发现:Big Nerd Ranch Guide 4th。我相信BNR的人,因为他们有iOS培训中心,他们不只是写另一本食谱。准确可能符合他们的最佳利益。

BNR示例一:addObserver:中的init:removeObserver:中的dealloc:

BNR示例二:addObserver:中的awakeFromNib:removeObserver:中的dealloc:

...在dealloc:移除观察者时,他们不使用[super dealloc];

我希望这有助于下一个人...

我正在更新这篇文章,因为Apple现在几乎完全没有使用Storyboard,所以上面提到的可能并不适用于所有情况。重要的是(以及我首先添加这篇文章的原因)是要注意你的viewWillDisappear:是否被调用。当应用程序进入后台时,它不适合我。

答案 10 :(得分:2)

接受的答案不安全,可能导致内存泄漏。请将取消注册保留在dealloc中,但也要在viewWillDisappear中取消注册(当然,如果您在viewWillAppear中注册)....这就是我所知道的并且它工作得很好! :)

答案 11 :(得分:1)

重要的是还要注意,当视图控制器呈现新的UIView时,也会调用viewWillDisappear。该委托只是表明视图控制器主视图在显示屏上不可见。

在这种情况下,如果我们使用通知允许UIview与父视图控制器通信,则在viewWillDisappear中取消分配通知可能会很不方便。

作为一种解决方案,我通常会在以下两种方法中删除观察者:

- (void)viewWillDisappear:(BOOL)animated {
    NSLog(@"viewController will disappear");
    if ([self isBeingDismissed]) {
        NSLog(@"viewController is being dismissed");
        [[NSNotificationCenter defaultCenter] removeObserver:self name:@"actionCompleted" object:nil];
    }
}

-(void)dealloc {
    NSLog(@"viewController is being deallocated");
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"actionCompleted" object:nil];
}

出于类似的原因,当我第一次发出通知时,我需要考虑这样一个事实,即任何时候出现在控制器上方的视图然后viewWillAppear方法被触发。这将反过来生成相同通知的多个副本。由于无法检查通知是否已处于活动状态,因此我会在添加通知之前删除通知来解决问题:

- (void)viewWillAppear:(BOOL)animated {
    NSLog(@"viewController will appear");
    // Add observers
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"imageGenerated" object:nil]; // This is added to avoid duplicate notifications when the view is presented again
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receivedImageFromCameraOrPhotolibraryMethodOnListener:) name:@"actionCompleted" object:nil];

}

答案 12 :(得分:0)

SWIFT 3

有两种使用通知的情况: - 只有当视图控制器在屏幕上时才需要它们; - 即使用户在当前打开另一个屏幕,也总是需要它们。

对于第一种情况,添加和删除观察者的正确位置是:

/// Add observers
///
/// - Parameter animated: the animation flag
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    NotificationCenter.default.addObserver(...)
}

/// Remove observers
///
/// - Parameter animated: the animation flag
override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    NotificationCenter.default.removeObserver(self)
}

对于第二种情况,正确的方法是:

/// Add observers
override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(...)
}

/// Remove observers
///
/// - Parameter animated: the animation flag
override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    if self.isBeingDismissed // remove only when view controller is removed disappear forever
    || !(self.navigationController?.viewControllers.contains(self) ?? true) {
        NotificationCenter.default.removeObserver(self)
    }
}

永远不会将removeObserver放入deinit{ ... } - 这是一个错误!

答案 13 :(得分:0)

override func viewDidLoad() {   //add observer
  super.viewDidLoad()
  NotificationCenter.default.addObserver(self, selector:#selector(Yourclassname.method), name: NSNotification.Name(rawValue: "NotificationIdentifier"), object: nil)
}

override func viewWillDisappear(_ animated: Bool) {    //remove observer
    super.viewWillDisappear(true)
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: "NotificationIdentifier"), object: nil)
}