web-dev-qa-db-ja.com

NSNotificationのオブザーバーが複数回呼び出されるのはなぜですか?

アプリ内では、いくつかのビューコントローラーを使用しています。 1つのビューコントローラーでは、オブザーバーは次のように初期化されます。

[[NSNotificationCenter defaultCenter] removeObserver:self name:@"MyNotification" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(myMethod:) name:@"MyNotification" object:nil];

初期化前にNSNotificationを削除する場合でも、myMethod:の実行回数は、それぞれのビューコントローラーで繰り返されるビューの数によって合計されます。

なぜこれが発生し、myMethod:を複数回呼び出さないようにすることができますか?.

注:postNotificationを複数回呼び出すときに間違いを犯さないように、ブレークポイントを使用して確認しました。

編集:これは私のpostNotificationがどのように見えるかです

NSArray * objects = [NSArray arrayWithObjects:[NSNumber numberWithInt:number],someText, nil];
NSArray * keys = [NSArray arrayWithObjects:@"Number",@"Text", nil];
NSDictionary * userInfo = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
[[NSNotificationCenter defaultCenter] postNotificationName:@"myNotification" object:self userInfo:userInfo];

編集:サブスクリプションをビューに移動した後も表示されます:同じ結果が得られます。 myMethod:は複数回呼び出されます。 (ビューコントローラーをリロードした回数)。

-(void)viewWillAppear:(BOOL)animated {

    [super viewWillAppear:animated];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"MyNotification" object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(myMethod:) name:@"MyNotification" object:nil];
}

編集:ライフサイクルに問題があるようです。 ViewDidUnloadとdeallocが呼び出されていませんが、viewdiddisappearが呼び出されています。

ビューコントローラーをスタックにプッシュする方法は次のとおりです。親はテーブルビューサブクラスです(行をクリックすると、このビューコントローラーが開始されます:

detailScreen * screen = [[detailScreen alloc] initWithContentID:ID andFullContentArray:fullContentIndex andParent:parent];
[self.navigationController pushViewController:screen animated:YES];

解決:

Nsnotificationの削除をviewdiddisappearに移動するとうまくいきました。ご指導ありがとうございます!

25
BarryK88

この説明に基づくと、考えられる原因は、ビューコントローラーが過剰に保持され、意図したとおりに解放されないことです。これは、物事が過剰に保持されている場合、ARCでもかなり一般的です。そのため、特定のビューコントローラのインスタンスが1つだけアクティブであると考えていますが、実際にはいくつかのライブインスタンスがあり、それらはすべて通知をリッスンします。

私がこの状況にあった場合、ビューコントローラーのdeallocメソッドにブレークポイントを設定し、それがアプリの意図した設計である場合は、正しく割り当て解除されていることを確認します。

37
Jaanus

どの方法でオブザーバーを登録しましたか?

Appleは、オブザーバーをviewWillAppear:に登録し、viewWillDissapear:で登録解除することを推奨しています

オブザーバーを2度登録しなくてもよろしいですか?

37
Igor

Swiftを実行しているアプリケーションでこの問題に遭遇しました。アプリケーションは、最初の起動時に一度通知を受け取りました。通知により、バックグラウンドに戻って戻ってくる回数が増えます。つまり

  • アプリが起動します-ビューに表示されるか、ビューがロードされると、オブザーバーが追加されます-通知が1回呼び出されます
  • アプリがバックグラウンドに戻り、ビューが表示されるか、ビューが読み込まれたときに、オブザーバーが再度呼び出されます。通知は2回呼び出されます。
  • この数は、バックグラウンドに戻って戻ってくる回数を増やします。
  • ビュー内のコードが消えても、ビューはまだウィンドウスタックにあり、ウィンドウスタックから削除されていないため、違いはありません。

解決策:アプリケーションがビューコントローラーでアクティブに辞任することを確認します。

  NSNotificationCenter.defaultCenter().addObserver(self, selector: "applicationWillResign:", name: UIApplicationWillResignActiveNotification, object: nil)

  func applicationWillResign(notification : NSNotification) {
    NSNotificationCenter.defaultCenter().removeObserver(self)
  }

これにより、ビューがバックグラウンドになったときに、ビューコントローラーが通知のオブザーバーを確実に削除します。

4
abhi

通知を購読している可能性が高いです

[[NSNotificationCenter defaultCenter] postNotificationName:@"myNotification" object:self userInfo:userInfo];

before selfが初期化されます。そして、実際にサブスクライブされていない 'self'をサブスクライブ解除しようとすると、すべてのグローバルmyNotification通知が表示されます。

ビューがIBに接続されている場合、通知を登録するための開始点として-awakeFromNib:を使用します

1

オブザーバーを持つクラスは、適切に複数回インスタンス化される可能性があります。デバッグ中は、通知が複数回投稿されているように見えます。しかし、selfを調べると、毎回異なるインスタンスであることがわかります。

アプリがタブバーを使用し、オブザーバーが、ビューコントローラーがサブクラスである基本クラスにいる場合、これは簡単に当てはまる可能性があります。

0
Murray Sagal