8

メソッド呼び出しの進行中であってもオブジェクトの割り当てが解除される可能性があることを考えると (リンク) *、オブジェクトが登録して、予想されるスレッドとは異なるスレッドで配信される通知を受け取ることは安全ですか?割り当て解除?

参考までに、ドキュメントには次のように記載されています

マルチスレッド アプリケーションでは、通知は常に、通知が投稿されたスレッドで配信されます。これは、オブザーバーが自身を登録したスレッドと同じではない場合があります。

また、NSNotificationCenter は、通知を受信するために登録されたオブジェクトへの強い参照を保持しないという事実も重要です。

状況をより具体的にする例を次に示します。

- (id)init {
    self = [super init];
    if (self) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:SomeNotification object:nil];
    }
}

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)handleNotification:(NSNotification *)notification {
    // do something
}

この実装を持つオブジェクトは、スレッド X で SomeNotification を受け取ります。

  1. 次のように考えるのは正しいですか。

    を。-handleNotification: を呼び出す前に NSNotificationCenter がオブジェクトへの強い参照を取得する場合、オブジェクトは -handleNotification: が戻るまで割り当て解除されません。

    b. -handleNotification: を呼び出す前に NSNotificationCenter がオブジェクトへの強い参照を取得しない場合、オブジェクトは -handleNotification: が戻る前に割り当て解除される可能性があります。

  2. どちらの方法 (a または b) で機能しますか? このトピックはまだドキュメントに記載されていませんが、マルチスレッド環境で NSNotificationCenter を安全に使用するためには、多少重要なようです。

更新:前述のリンクの回答が更新され、「ARC は弱参照での呼び出しを保持および解放する」ことが示されました。これは、メソッド呼び出しの進行中にオブジェクトの割り当てを解除してはならないことを意味します。

4

2 に答える 2

8

メイン以外のスレッドで通知が飛んでいて、バックグラウンドで割り当て解除が発生している場合は、スレッドが複雑すぎる可能性があることを常にお勧めします。ObjC はスレッドに適した言語ではありません。ほとんどのスレッド化作業は、キュー上の短命のブロックの形式にする必要があります。通知をメイン スレッドに簡単に戻すことができますが、通知を頻繁に消費するべきではありません。

とはいえ、現在マルチスレッド通知を管理する最良の方法はaddObserverForName:object:queue:usingBlock:. これにより、ライフタイムをより細かく制御できます。パターンは次のようになります。

__weak id weakself = self;
id notificationObserver = [[NSNotificationCenter defaultCenter]
 addObserverForName:...
 object:...
 queue:[NSOperationQueue mainQueue]
 usingBlock:^(NSNotification *note){
   id strongself = weakself;
   if (strongself) {
     [strongself handleNotification:note];
   }
 }];

weakself/strongself の使用に注意してください。weakself を使用して保持ループを回避しています。戻ってきたら、最初に強力な参照を取得します。まだ存在する場合は、ブロックの残りの部分にロックインされます (したがって、ブロックを解除することはできませんhandleNotification:)。存在しない場合、通知は破棄されます。(弱い参照は、 を呼び出す前に事実上ゼロになることに注意してください。 objc_loadWeakdeallocを参照してください。)ここで使用していますが、別のキューを使用することもできます。mainQueue

「昔」(10.6 より前) には、オブジェクトの有効期間を制御することで、この問題を回避するように設計しました。基本的に、短命のオブジェクトが他のスレッドからの通知をリッスンしないように設計しました。10.6 より前のコードでは、スレッド化を非常にまれに保つことができるため、これは思ったよりもはるかに簡単でした (そして、IMO は依然として低レベルに保つ必要があります)。NSNotificationCenterそのロースレッドの世界のために設計されました。

-[NSNotificationCenter addObserver:selector:name:object:]また、 とは異なり、-[NSNotificationCenter addObserverForName:object:queue:usingBlock:]オブザーバーとして機能する不透明なオブジェクトを返すことにも注意してください。後でオブザーバーを削除できるように、このオブジェクトを追跡する必要があります。

于 2012-12-06T15:03:26.913 に答える