2010-12-29 9 views
27

私はまだobjective-cのブロックには新しく、この擬似コードが正しいかどうか疑問に思っています。私はそれだけでオブザーバを削除するか、私はremoveObserverを呼び出す必要がある場合には十分だかはわからない:名前:オブジェクト:addObserverForNameの正しい管理:オブジェクト:キュー:usingBlock:

-(void) scan { 
    Scanner *scanner = [[Scanner alloc] init]; 
    id scanComplete = [[NSNotificationCenter defaultCenter] addObserverForName:@"ScanComplete" 
         object:scanner 
         queue:nil 
         usingBlock:^(NSNotification *notification){ 
          /* 
          do something 
          */ 
          [[NSNotificationCenter defaultCenter] removeObserver:scanComplete]; 
          [scanner release]; 
         }]; 
    [scanner startScan]; 
} 

アップデート:私はこのブロックからの断続的なEXC_BAD_ACCESSを受けていますので、これをすることはできません右。

答えて

48

ブロック自体を定義する前に、変数scanCompleteを宣言してください。

これは、変数自体がまだ割り当てられていないため、定義時にブロック内に存在しない変数にアクセスしようとしているためです。

EXC_BAD_ACCESSとは何ですか?まあ、それは存在しない参照にアクセスしようとするとスローされる例外です。あなたの例ではまさにそうです。

ですから、ブロック自体の前に変数を宣言した場合、それが動作するはずです:

-(void) scan { 
    Scanner *scanner = [[Scanner alloc] init]; 
    __block id scanComplete; 
    scanComplete = [[NSNotificationCenter defaultCenter] addObserverForName:@"ScanComplete" 
         object:scanner 
         queue:nil 
         usingBlock:^(NSNotification *notification){ 
          /* 
          do something 
          */ 
          [[NSNotificationCenter defaultCenter] removeObserver:scanComplete]; 
          [scanner release]; 
        }]; 
    [scanner startScan]; 
} 
+0

あなたは '__block id scanComplete;'が必要です。そうしないとブロックにコピーされ、オブザーバが漏れてしまいます。 – hwaxxer

+3

ARCの世界では、キャプチャを避けるために '__block'を使用するコメントはもはや真実ではありません。 _does_が真であることは、 '__block'修飾子が基本的な問題を修正したことです:ブロックが定義されているとき、' addObserverForName:...'はまだ返されていないので、捕捉される値は、(変数宣言の暗黙的なauto-nilのためにARCの下で実行されているとき)最高でnilであるか、または**未定義**で、完全に未定義の振る舞いについてBAD_ACCESS 、最悪の場合... – danyowdee

+0

ブロック内のローカル変数を参照してオブザーバを削除することは、常にskankyでした。返されたオブザーバトークン(ここでは 'scanComplete')をインスタンス変数として格納します。 ARCの下では、これは '__weak'インスタンス変数でなければなりません。 – matt

-4

ブロックの範囲をスキャナオブジェクトを解放する権限を持っていません。ガベージコレクションを使用していない場合は、releaseを削除し、スキャナを自動解放する([[[Scanner alloc] init] autorelease])ようにしてください。

また、ブロック外の電話をremoveObserverに安全に移動することができます。

EXC_BAD_ACCESSの場合:アプリケーションがクラッシュした後にコンソールウィンドウにbtと入力すると、バックトレースが表示され、エラーが発生した場所を通知する必要があります。

15

登録ブロックで登録を解除しないでください。代わりに、addObserverForName(この場合はscanComplete)から返されたトークンをインスタンス変数として、またはインスタンス変数であるコレクションに格納し、後で廃止しようとしているときに登録解除してください(例:dealloc)。私が行うことは、observersと呼ばれるNSMutableSetを保持することです。だから、:

id ob = [[NSNotificationCenter defaultCenter] 
    addObserverForName:@"whatever" object:nil queue:nil 
    usingBlock:^(NSNotification *note) { 
     // ... whatever ... 
}]; 
[self->observers addObject:ob]; 

そしてそれ以降:この方法について

for (id ob in self->observers) 
    [[NSNotificationCenter defaultCenter] removeObserver:ob]; 
self->observers = nil; 
+4

ワンショット通知が必要な場合は、なぜブロック自体で登録を解除できないべきかわかりません。 – ipmcc

3

アップルのドキュメント:

次の例では、ロケール変更通知を受け取るように登録する方法を示しています。

NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; 
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue]; 
self.localeChangeObserver = [center addObserverForName:NSCurrentLocaleDidChangeNotification object:nil 
    queue:mainQueue usingBlock:^(NSNotification *note) { 

     NSLog(@"The user's locale changed to: %@", [[NSLocale currentLocale] localeIdentifier]); 
    }]; 

観測の登録を解除するには、removeObserverに、このメソッドによって返されるオブジェクトを渡す:. でなければなりませんremoveObserver:またはremoveObserver:name:object:addObserverForName:object:queue:usingBlock:で指定されたオブジェクトの前に割り当てが解除されます。

NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; 
[center removeObserver:self.localeChangeObserver];