6

私はARCを使用しており[[NSNotificationCenter defaultCenter] removeObserver:someObserver];、オブザーバーのdealloc.

NSNotificationCenter クラスリファレンスから

notificationObserver または addObserver:selector:name:object: で指定されたオブジェクトの割り当てが解除される前に、必ずこのメソッド (または removeObserver:name:object:) を呼び出してください。

NSNotificationCenter はオブザーバーを保持しません。

Q1:NSNotificationCenterスレッドセーフですか?

場合によっては、オブザーバーの割り当てが解除され (通知センターからオブザーバーが削除され)、別のスレッドが同時に通知を投稿します。

ランダムなクラッシュが発生しましたが、これが原因だと思います。

Q2: このような状況はありえますか?

Q3: につながるのEXC_BAD_ACCESS

Q4: では[[NSNotificationCenter defaultCenter] removeObserver:someObserver];、オブザーバーを呼び出しても安全deallocですか?

Q5: 安全でない場合は、どこに電話すればよいremoveObserver:ですか?

4

3 に答える 3

2

私は同じことを疑問に思いました、そして私はそれが文書化されているのを見つけることができません。これが私が起こっていると思うことです。

removeObserver:あなたが望むようにスレッドセーフではありません。

次のような状況を考えてください。オブザーバーへの最後の参照は、スレッドAでコードを実行しているときに解放されます。スレッドAは、オブザーバーのdeallocメソッドを呼び出します。同時に、監視対象オブジェクトは[NSNotificcationCenter postNotificationName:object:]スレッドBからを実行します。これにより、避けられない競合状態が発生します。つまり、オブジェクトがそのメソッド内にある間、通知が送信されます。dealloc

- (void)init {
    ...
    [[NSNotificcationCenter defaultCenter] addObserver:self
                                              selector:@selector(callback:)
                                                  name:@"whatever"
                                                object:nil];
    ...
}

- (void)dealloc {

    // If the observed object posts the notification on thread B while 
    // thread A is here, there's a race! At best, thread B will be
    // in callback: while thread A is here in dealloc. That's probably
    // not what you expect. The worst case is that thread B won't make
    // make it to callback: until after thread A completes the dealloc
    // and the memory has been freed. Likely crash!

    [[NSNotificationCenter defaultCenter] removeObserver:self];

    // If the observed object does the post when thread A is here,
    // you'll be fine since the observation has been removed.
}

これは、他のメインスレッドオブジェクトのみを監視しているメインスレッドオブジェクトの場合は問題になりません。これは、定義上、説明したスレッドAおよびBのシナリオに入ることができないためです。

マルチスレッドの場合、問題を回避することを保証する唯一の方法は、オブザーバーのrefcountが0になる前に、オブザベーションが停止することを確認することです。 、 それは簡単です。そうでなければ、私は解決策を知りません。termclose

于 2013-02-08T22:11:02.917 に答える
2

はい、NSNotificationCenterオブザーバーは保持されませんが、ディスパッチ テーブルにはオブザーバーへのポインターがまだあります。

Q1: Apple ドキュメントの引用

通常の通知センターは、通知が投稿されたスレッドで通知を配信します。分散通知センターは、メイン スレッドで通知を配信します。場合によっては、通知センターではなく、ユーザーが決定した特定のスレッドで通知を配信する必要がある場合があります。たとえば、バックグラウンド スレッドで実行されているオブジェクトが、ウィンドウを閉じるなどのユーザー インターフェイスからの通知をリッスンしている場合、メイン スレッドではなくバックグラウンド スレッドで通知を受け取りたいと考えます。このような場合、デフォルト スレッドで配信される通知をキャプチャし、適切なスレッドにリダイレクトする必要があります。

Q2,3: はい。

Q4,5: 循環参照に出くわさない限り安全です。私は通常、UIViewControllers の/と他のクラスの-viewWillAppear:/を追加/削除します。-viewWillDisappear:-initdealloc

于 2012-12-17T12:51:14.347 に答える