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

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

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

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

一般的答案是“只要你不再需要通知”。 这显然不是一个令人满意的答案。

我build议你在那些打算用作观察者的类的dealloc方法中添加一个调用[notificationCenter removeObserver: self] ,因为这是最后一次干净地注销观察者的机会。 但是,这样做只会保护您免于因通知中心通知死亡对象而导致崩溃。 当对象尚未/不再处于可以正确处理通知的状态时,它不能保护您的代码免受通知。 为此…见上文。

编辑 (因为答案似乎比我想象得到更多的意见)我想在这里说的是:关于什么时候最好从通知中心删除观察员是非常困难的,因为这取决于:

  • 在您的使用案例(哪些通知被观察?他们什么时候发送?)
  • 观察者的实现(何时准备好接收通知?何时不再准备好?)
  • 观察者的预期生命周期(是否与某个其他对象,比如视图或视图控制器绑定)?

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

你总是可以安全的removeObserver:一个对象多次(除了第一个给定的观察者的呼叫都是nops)。 所以:在dealloc想一下,但是首先要做的是:在适当的时候(这取决于你的用例)。

注意:这已经过testing和工作的百分之百

迅速

 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) } } 

Objective-C的

iOS 6.0 > version ,更好地移除viewWillDisappear中的观察者将viewWillDisappear因为viewDidUnload方法已被弃用。

  [[NSNotificationCenter defaultCenter] removeObserver:observerObjectHere]; 

navigation stack or hierarchy删除视图时,有多less次更好地remove observer

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

从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

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

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

一般我把它放到dealloc方法。

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

 deinit { ... } 

Swift文档:

在解除分配类实例之前立即调用取消初始化程序。 你可以使用deinit关键字来编写deinitializers,类似于init关键字的初始化函数。 取消初始化器仅适用于类types。

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

*编辑:这个build议适用于iOS <= 5(甚至你应该添加viewWillAppearviewWillAppear删除 – 但是,如果由于某种原因,你已经添加了观察者在viewDidLoad的build议适用)

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

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

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

iOS 6中 ,删除viewDidUnload中的观察者也没有意义,因为现在已经废弃了。

总而言之,我总是把它viewDidDisappear 。 不过,这也取决于你的要求,就像@Dirk说的那样。

我想我find了一个可靠的答案 ! 我不得不,因为上面的答案是模棱两可的,似乎矛盾。 我浏览了食谱和编程指南。

首先, addObserver:的风格addObserver:viewWillAppear:removeObserver:viewWillDisappear:不适用于我(我testing了它),因为我发布通知的子视图控制器来执行父视图控制器中的代码。 如果我在相同的视图控制器中发布和侦听通知,我只会使用这种风格。

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

BNR例子一: addObserver:init:removeObserver:dealloc:

BNR例子二: addObserver:awakeFromNib:removeObserver:dealloc:

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

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

我正在更新这篇文章,因为苹果现在已经几乎完全与故事板走了,所以上述可能不适用于所有情况。 重要的事情(和我添加这篇文章的原因)首先要注意,如果你的viewWillDisappear:被调用。 应用程序进入后台时不适合我。

接受的答案是不安全的,可能会导致内存泄漏。 请不要在dealloc中取消注册,但也可以取消注册将viewDisappear(这当然如果你注册viewWillAppear)….这是我的经验,它的工作伟大! 🙂

当view controller显示一个新的UIView时,还要注意viewWillDisappear也被调用。 这个委托只是简单地表明视图控制器主视图在显示屏上不可见。

在这种情况下,如果我们使用通知来允许UIview与父视图控制器进行通信,则在viewWillDisappear解除分配通知可能是不方便的。

作为一个解决scheme,我通常在这两种方法之一中删除观察者:

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

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{ ... } – 这是一个错误!

 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) }