web-dev-qa-db-ja.com

iOS通知からオブザーバーを削除:すべてのオブザーバーに対してこれを1回呼び出すことはできますか?そして、たとえ何もない場合でも?

ほとんどのView Controllerに3人のオブザーバーを登録しています。いくつかはより多く、いくつかはより少ないが、私は登録と登録解除プロセスの一部を親クラスに含めたい。オブザーバーがいない場合でも、登録解除の呼び出しに問題はありますか?そして、3人のオブザーバーすべての登録を解除するだけで十分ですか?

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

はい、オブザーバーがselfであるすべての登録を削除します。 NSNotificationCenter Class Reference に文書化されています。

次の例は、以前に登録したすべての通知のsomeObserverを登録解除する方法を示しています。

[[NSNotificationCenter defaultCenter] removeObserver:someObserver];

理論上(ただし、実際にはiOS 7.0の時点ではそうではありません)、UIViewControllerにはviewWillDisappear:で削除したくない独自の登録がある可能性があることに注意してください。 addObserver:selector:name:object:を使用してパブリックAPIの通知に登録することはほとんどありません。これは、UIViewControllerサブクラスに登録することを妨げるためです。将来のバージョンで。

登録を解除する安全な方法は、登録ごとにremoveObserver:name:object:を1回送信することです。

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

別の方法は、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];
}

addObserverForName:object:queue:usingBlock:によって返されるすべてのオブザーバーは、登録が1つだけの新しいオブジェクトであるため、removeObserver:を呼び出すたびに、そのオブザーバーの1つの登録のみが削除されることが保証されます。

IOS 9/macOS 10.11以降のアップデート

IOS 9およびmacOS 10.11では、オブザーバーの割り当てが解除されると、NSNotificationCenterは自動的にオブザーバーの登録を解除します。デプロイメントターゲットがiOS 9以降またはmacOS 10.11以降の場合、deallocメソッド(またはSwiftのdeinit)で手動で登録を解除する必要はありません。

58
rob mayoff

オブザーバーがいない場合でも最初の質問の登録は解除できます。ただし、オブザーバーを削除する方法については、[[NSNotificationCenter defaultCenter] removeObserver:someObserver];は非常に推奨されないスーパークラスのオブザーバーも削除します(オブジェクトがアンロードされるためdeallocを除く)が、viewWillDisappearではオブザーバーを削除する必要があります[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];を使用して1つずつ

7
MAB