web-dev-qa-db-ja.com

addObserverForName:object:queue:usingBlockの正しい管理:

私はまだObjective-cのブロックに慣れておらず、この擬似コードが正しいかどうか疑問に思っています。オブザーバーを削除するだけで十分かどうか、またはremoveObserver:name:object:を呼び出す必要があるかどうかはわかりません。

-(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を受信して​​いるため、これは正しくありません。

28
seanalltogether

ブロック自体を定義する前に、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];
}
48
Jacob Relkin

登録ブロックで登録を解除しないでください。代わりに、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;
15
matt

この方法に関するAppleドキュメント:

次の例は、ロケール変更通知を受信するために登録する方法を示しています。

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:に渡します。あなた必須 addObserverForName:object:queue:usingBlock:で指定されたオブジェクトの割り当てが解除される前に、removeObserver:またはremoveObserver:name:object:を呼び出します。

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