NSNotificationをその場限りのcallbackっぽく使いたい。積極的に使うべきとは思いませんが、すでにNSNotificationが飛び交っているようなアプリケーションでは、このイディオムを使う機会もあると思います。
実装はこちら。 __block __weak
のあたりがアヤシイですが、両方とも必要です。
__block __weak id observer;
observer = [NSNotificationCenter.defaultCenter addObserverForName:@"foo"
object:nil
queue:nil
usingBlock:^(NSNotification *note)
{
[NSNotificationCenter.defaultCenter removeObserver:observer];
NSLog(@"catch in %@!", observer);
}];
挙動はこんな感じで確認した。
// in AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
__block __weak id observer;
observer = [NSNotificationCenter.defaultCenter addObserverForName:@"foo"
object:nil
queue:nil
usingBlock:^(NSNotification *note)
{
[NSNotificationCenter.defaultCenter removeObserver:observer];
NSLog(@"catch in %@!", observer);
}];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"post!");
[NSNotificationCenter.defaultCenter postNotificationName:@"foo" object:nil];
NSLog(@"posted for %@!", observer);
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"post!");
[NSNotificationCenter.defaultCenter postNotificationName:@"foo" object:nil];
NSLog(@"posted for %@!", observer);
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"post!");
[NSNotificationCenter.defaultCenter postNotificationName:@"foo" object:nil];
NSLog(@"posted for %@!", observer);
});
NSLog(@"observer %@", observer);
return YES;
}
ログはこんな感じ:
2014-03-24 17:29:46.779 NotificationTest[20952:60b] observer <__NSObserver: 0x109500380>
2014-03-24 17:29:47.780 NotificationTest[20952:60b] post!
2014-03-24 17:29:47.781 NotificationTest[20952:60b] catch in <__NSObserver: 0x109500380>!
2014-03-24 17:29:47.782 NotificationTest[20952:60b] posted for (null)!
2014-03-24 17:29:48.780 NotificationTest[20952:60b] post!
2014-03-24 17:29:48.781 NotificationTest[20952:60b] posted for (null)!
2014-03-24 17:29:49.779 NotificationTest[20952:60b] post!
2014-03-24 17:29:49.780 NotificationTest[20952:60b] posted for (null)!
__weakがないと開放されないので注意:
2014-03-24 17:20:58.828 NotificationTest[7003:60b] observer <__NSObserver: 0x109377a70>
2014-03-24 17:20:59.829 NotificationTest[7003:60b] post!
2014-03-24 17:20:59.830 NotificationTest[7003:60b] catch in <__NSObserver: 0x109377a70>!
2014-03-24 17:20:59.830 NotificationTest[7003:60b] posted for <__NSObserver: 0x109377a70>!
2014-03-24 17:21:00.829 NotificationTest[7003:60b] post!
2014-03-24 17:21:00.830 NotificationTest[7003:60b] posted for <__NSObserver: 0x109377a70>!
2014-03-24 17:21:01.829 NotificationTest[7003:60b] post!
2014-03-24 17:21:01.829 NotificationTest[7003:60b] posted for <__NSObserver: 0x109377a70>!
また、__blockがないとusingBlock:でblockオブジェクトを作るときのobserverの値が単にコピーされるため、ブロック内のobserverは常にnullになりobserver解除がうまくいかなくなる。