目标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将不再向可能被释放的注册观察者发送通知。
如果观察者被添加到视图控制器 ,我强烈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(甚至你应该添加viewWillAppear
和viewWillAppear
删除 – 但是,如果由于某种原因,你已经添加了观察者在viewDidLoad
的build议适用)
如果您在viewDidLoad
添加了观察者,则应该在dealloc
和viewDidUnload
中将其删除。 否则,当viewDidLoad
在viewDidUnload
之后被调用时(这会在内存警告之后发生),最终会添加两次。 在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) }