LoginSignup
8
9

More than 5 years have passed since last update.

一度だけ呼び出されるNSNotificationのobserverを定義する

Last updated at Posted at 2014-03-24

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解除がうまくいかなくなる。

8
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
9