104

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

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

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

4

14 回答 14

112

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

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

编辑(因为答案似乎比我想象的要多)我在这里想说的是:很难就何时最好从通知中心删除观察者给出一般性建议,因为这取决于:

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

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

你总是可以安全地removeObserver:多次调用一个对象(除了给定观察者的第一次调用之外,所有调用都是 nops)。所以:考虑一下(再次)这样做dealloc,但首先是:在适当的时候做(这取决于你的用例)。

于 2011-06-24T14:32:18.493 回答
39

注意:这已经过测试并且可以 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)
    }
}

呈现视图控制器:

override func viewWillDisappear(animated: Bool){
    super.viewWillDisappear(animated)
    
    if self.isBeingDismissed()  //presented view controller
    {
        // remove observer here
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
}

Objective-C

在中,不推荐使用as方法中的iOS 6.0 > version观察者。viewWillDisappearviewDidUnload

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

呈现视图控制器:

- (void)viewWillDisappear:(BOOL)animated{
    if ([self isBeingDismissed] == YES) ///presented view controller
    {
        // remove observer here
        [[NSNotificationCenter defaultCenter] removeObserver:observerObjectHere];
    }
}
于 2013-04-11T05:26:29.363 回答
39

从 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

于 2016-04-24T11:23:35.703 回答
25

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

于 2012-08-08T15:27:11.830 回答
20
-(void) dealloc {
      [[NSNotificationCenter defaultCenter] removeObserver:self];
      [super dealloc];
}
于 2011-06-24T14:35:29.760 回答
7

一般来说,我把它放入dealloc方法中。

于 2011-06-24T14:31:34.127 回答
6

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

deinit {
    ...
}

斯威夫特文档:

在释放类实例之前立即调用 deinitializer。您使用 deinit 关键字编写反初始化器,类似于使用 init 关键字编写初始化器的方式。反初始化器仅适用于类类型。

通常,当您的实例被解除分配时,您不需要执行手动清理。但是,当您使用自己的资源时,您可能需要自己执行一些额外的清理工作。例如,如果您创建一个自定义类来打开一个文件并向其中写入一些数据,您可能需要在释放类实例之前关闭该文件。

于 2015-01-22T07:45:34.287 回答
5

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

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

于 2012-11-25T12:17:21.653 回答
5

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

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

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

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

于 2013-01-16T05:51:15.923 回答
4

我想我找到了一个可靠的答案!我不得不这样做,因为上面的答案模棱两可,似乎自相矛盾。我浏览了 Cookbooks 和 Programming Guides。

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

我最依赖的答案,我在 iOS Programming: Big Nerd Ranch Guide 4th 中找到的。我相信 BNR 的人,因为他们有 iOS 培训中心,而且他们不仅仅是在写另一本食谱。准确可能符合他们的最大利益。

BNR 示例一:addObserver:in init:, removeObserver:indealloc:

BNR 示例二:addObserver:in awakeFromNib:, removeObserver:indealloc:

…当删除观察者时,dealloc:他们不使用[super dealloc];

我希望这可以帮助下一个人……</p>

我正在更新这篇文章,因为 Apple 现在几乎完全放弃了 Storyboard,因此上述内容可能不适用于所有情况。重要的是(以及我首先添加此帖子的原因)是要注意您viewWillDisappear:是否被调用。当应用程序进入后台时,它不适合我。

于 2014-06-13T13:44:19.223 回答
2

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

于 2013-06-27T20:03:08.663 回答
1

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

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

作为一种解决方案,我通常使用以下两种方法之一删除观察者:

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

}
于 2017-01-22T02:53:04.260 回答
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)
}
于 2017-07-31T05:51:02.767 回答
-1

这是唯一正确的答案(所有其他都被错误地建议使用deinit-dealloc这是对类实例生命周期和 iOS 系统的明显误解)。

斯威夫特 5

使用通知有两种情况:

  • 当视图控制器在屏幕上时才需要它们( viewWillAppear + viewWillDisappear)
  • 即使用户当前打开了另一个屏幕(使用“if”) ,也始终需要它们。viewDidLoad + viewWillDisappear

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

    /// Add observers
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        NotificationCenter.default.addObserver(...)
    }

    /// Remove observers
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)

// remove when screen dismissed
        NotificationCenter.default.removeObserver(self) 
    }

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

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

    /// Remove observers
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)

// remove only when view controller is removed disappear forever
        if self.isBeingDismissed 
        || !(self.navigationController?.viewControllers.contains(self) ?? true) {
            NotificationCenter.default.removeObserver(self)
        }
    }

并且永远不要removeObserver输入deinit{ ... },或者dealloc-这是一个错误!

于 2017-07-03T10:06:15.640 回答