iOS 从通知中删除观察者:我可以为所有观察者调用一次吗?即使没有?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21418726/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
iOS Remove observer from notification: Can I call this once for all observers? And even if there are none?
提问by MichiZH
I'm registering three observers in most of my view controllers. Some have more, some less but I want to include part of the registration and unregistration process in a parent class. Is there any problem with calling the unregistering even if there is no observer? And is one call to unregister enough for all three observers?
我在大多数视图控制器中注册了三个观察者。有些更多,有些更少,但我想在父类中包含部分注册和取消注册过程。即使没有观察者,调用取消注册有什么问题吗?对于所有三个观察者来说,一次取消注册就足够了吗?
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWasShown:)
name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillBeHidden:)
name:UIKeyboardWillHideNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationWillEnterBackground:)
name:UIApplicationWillResignActiveNotification
object:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
//Has to be unregistered always, otherwise nav controllers down the line will call this method
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
回答by rob mayoff
Yes, that will remove all registrations where the observer is self
. It's documented in the NSNotificationCenter Class Reference:
是的,这将删除观察者所在的所有注册self
。它记录在NSNotificationCenter 类参考中:
The following example illustrates how to unregister
someObserver
for all notifications for which it had previously registered:[[NSNotificationCenter defaultCenter] removeObserver:someObserver];
以下示例说明了如何取消注册
someObserver
之前注册的所有通知:[[NSNotificationCenter defaultCenter] removeObserver:someObserver];
Note that in theory (but not, as far as I know, in practice as of iOS 7.0), UIViewController
could have its own registrations that it doesn't want removed in viewWillDisappear:
. It's unlikely to register for any of the notifications in the public API using addObserver:selector:name:object:
, because that would preclude you registering for them in your UIViewController
subclass, but it could certainly register for non-public notifications now or in a future version.
请注意,理论上(但据我所知,从 iOS 7.0 开始,在实践UIViewController
中并非如此),可以拥有自己不想在viewWillDisappear:
. 不太可能使用 注册公共 API 中的任何通知addObserver:selector:name:object:
,因为这会阻止您在UIViewController
子类中注册它们,但它现在或在未来版本中肯定可以注册非公共通知。
A safe way to deregister is to send removeObserver:name:object:
once for each registration:
一种安全的注销方法是removeObserver:name:object:
为每个注册发送一次:
- (void)deregisterForKeyboardNotifications {
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[center removeObserver:self name:UIKeyboardWillHideNotification object:nil];
[center removeObserver:self name:UIApplicationWillResignActiveNotification object:nil];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self deregisterForKeyboardNotifications];
}
- (void)dealloc {
[self deregisterForKeyboardNotifications];
}
Another way is to use addObserverForName:object:queue:usingBlock:
to register (instead of addObserver:selector:name:object:
). This returns a new observer object reference for each registration. You have to save these away (perhaps in an NSArray
instance variable if you don't want to create individual instance variables). Then you pass each one to removeObserver:
to deregister its notification. Example:
另一种方法是使用addObserverForName:object:queue:usingBlock:
注册(而不是addObserver:selector:name:object:
)。这将为每个注册返回一个新的观察者对象引用。您必须将它们保存起来(NSArray
如果您不想创建单个实例变量,则可能保存在实例变量中)。然后您将每个传递removeObserver:
给以注销其通知。例子:
@implementation MyViewController {
NSMutableArray *observers;
}
- (void)registerForKeyboardNotifications {
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
NSOperationQueue *queue = [NSOperationQueue mainQueue];
__weak MyViewController *me = self;
observers = [NSMutableArray array];
[observers addObject:[center addObserverForName:UIKeyboardWillShowNotification
object:nil queue:queue usingBlock:^(NSNotification *note) {
[me keyboardWillShow:note];
}]];
[observers addObject:[center addObserverForName:UIKeyboardWillHideNotification
object:nil queue:queue usingBlock:^(NSNotification *note) {
[me keyboardWillHide:note];
}]];
[observers addObject:[center addObserverForName:UIApplicationWillResignActiveNotification
object:nil queue:queue usingBlock:^(NSNotification *note) {
[me applicationWillResignActive:note];
}]];
}
- (void)deregisterForKeyboardNotifications {
for (id observer in observers) {
[[NSNotificationCenter defaultCenter] removeObserver:observer];
}
observers = nil;
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self deregisterForKeyboardNotifications];
}
- (void)dealloc {
[self deregisterForKeyboardNotifications];
}
Since every observer returned by addObserverForName:object:queue:usingBlock:
is a new object that has only one registration, each call to removeObserver:
is guaranteed to only remove that observer's one registration.
由于返回的每个观察者addObserverForName:object:queue:usingBlock:
都是一个只有一个注册的新对象,因此每次调用removeObserver:
都保证只会删除该观察者的一个注册。
Update for iOS 9 / macOS 10.11 and later
iOS 9 / macOS 10.11 及更高版本的更新
As of iOS 9 and macOS 10.11, NSNotificationCenter
automatically deregisters an observer if the observer is deallocated. It is no longer necessary to deregister yourself manually in your dealloc
method (or deinit
in Swift) if your deployment target is iOS 9 or later or macOS 10.11 or later.
从 iOS 9 和 macOS 10.11 开始,NSNotificationCenter
如果观察者被解除分配,则自动取消注册观察者。如果您的部署目标是 iOS 9 或更高版本或 macOS 10.11 或更高版本,则不再需要在您的dealloc
方法中(或deinit
在 Swift 中)手动取消注册。
回答by MAB
For your first Question unregistering even when there is no observer is OK.
But for the way you're removing the observer, [[NSNotificationCenter defaultCenter] removeObserver:someObserver];
will remove even the super class observers which is highly unrecommended (except in dealloc because the object is unloaded) but in viewWillDisappear
you should remove the observers one by one by using [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
对于您的第一个问题,即使没有观察者也可以取消注册。但是对于您删除观察者的方式,[[NSNotificationCenter defaultCenter] removeObserver:someObserver];
甚至会删除非常不推荐的超类观察者(在 dealloc 中除外,因为对象已卸载)但在viewWillDisappear
您应该使用以下方法删除观察者[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];