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

Zhe*_*hen 101 objective-c nsnotifications ios

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

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

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

Dir*_*irk 112

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

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

编辑(因为答案似乎吸引了比我想象的更多的评论)我在这里想说的是:关于什么时候最好从通知中心删除观察者,很难给出一般性的建议,因为这取决于:

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

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

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

  • ARC不安全,可能导致泄漏.请参阅此文章:http://www.cocoabuilder.com/archive/cocoa/311831-arc-and-dealloc.html (4认同)
  • @MobileMon你链接的文章似乎说明了我的观点.我错过了什么? (3认同)

Par*_*iya 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)
    }
}
Run Code Online (Sandbox Code Playgroud)

PresentedViewController

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

    if self.isBeingDismissed()  //presented view controller
    {
        // remove observer here
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
}
Run Code Online (Sandbox Code Playgroud)

Objective-C的

iOS 6.0 > version,不推荐使用viewWillDisappearas viewDidUnload方法删除观察者.

 [[NSNotificationCenter defaultCenter] removeObserver:observerObjectHere];
Run Code Online (Sandbox Code Playgroud)

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];
    }
}
Run Code Online (Sandbox Code Playgroud)

PresentedViewController

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

  • 除非控制器在其视图*不*显示时仍可能需要通知(例如,重新加载tableView). (8认同)
  • @wcochran在`viewWillAppear:`中自动重新加载/刷新 (2认同)

Seb*_*ian 38

从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

  • 链接的文档详细介绍了这一点.TL; DR:这是一个弱参考. (5认同)
  • 可能他们不会向观察者发送信息,但我相信他们会像我所理解的那样对他们进行强有力的参考.在这种情况下,所有观察者都将留在记忆中并产生泄漏.如果我错了,请告诉我. (2认同)

Ric*_*kiG 25

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

  • 在我尝试的那些中,使用iOS7时,这是在使用UIViewControllers时注册/删除观察者的最佳方法.唯一的问题是,在许多情况下,您不希望在使用UINavigationController并将另一个UIViewController推送到堆栈时删除观察者.解决方案:您可以通过调用[self isBeingDismissed]来检查是否在viewWillDisappear中弹出了VC. (4认同)
  • @IsaacOveracker有几个原因:你的设置代码(例如loadView和viewDidLoad)可能会导致通知被触发,你的控制器需要在它显示之前反映出来.如果你这样做有一些好处.目前你决定"离开"控制器,你不关心通知,他们不会让你在控制器被推离屏幕等时做逻辑.有些特殊情况下控制器应该接收通知屏幕外我想你不能这样做.但是这样的事件应该在你的模型中. (2认同)

Leg*_*las 19

-(void) dealloc {
      [[NSNotificationCenter defaultCenter] removeObserver:self];
      [super dealloc];
}
Run Code Online (Sandbox Code Playgroud)

  • 如果在iOS 5+上使用ARC,我认为不再需要`[super dealloc]` (38认同)
  • 我将这些指令的顺序转向...在`[super dealloc]`之后使用`self`让我感到紧张......(即使接收器不太可能以任何方式实际取消引用指针,嗯,你永远不会知道,他们如何实施`NSNotificationCenter`) (4认同)
  • @pixelfreak更强,在ARC下不允许调用[super dealloc] (3认同)

Rap*_*sso 8

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


Mor*_*ard 7

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

deinit {
    ...
}
Run Code Online (Sandbox Code Playgroud)

Swift文档:

在取消分配类实例之前立即调用deinitializer.您使用deinit关键字编写deinitializers,类似于使用init关键字编写初始化器的方式.Deinitializers仅适用于班级类型.

通常,在取消分配实例时,您无需执行手动清理.但是,当您使用自己的资源时,可能需要自己执行一些额外的清理.例如,如果创建自定义类以打开文件并向其写入一些数据,则可能需要在取消分配类实例之前关闭该文件.


Ehr*_*ren 5

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

如果你已经添加的观察者viewDidLoad,你应该在这两个删除deallocviewDidUnload.否则你最后会在viewDidLoad调用之后添加两次viewDidUnload(这将在内存警告之后发生).这在iOS 6中不是必需的,viewDidUnload不推荐使用并且不会被调用(因为视图不再自动卸载).

  • 欢迎来到StackOverflow.请查看MarkDown常见问题解答(问题/答案编辑框旁边的问号图标).使用Markdwon将提高您答案的可用性. (2认同)

kim*_*aro 5

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

- (void)dealloc
{
      [[NSNotificationCenter defaultCenter] removeObserver:self];
      [super dealloc];
}
Run Code Online (Sandbox Code Playgroud)

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

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