2013/03/10

NSNotificationCenterの通知を受け取るときにちょっとハマったケース

非同期で処理を行いたい時には欠かせないNSNotificationCenterですが、とあるケースで通知が受け取れずにハマったので、ブログに書いておこうと思います。

NSNotificationCenterを使う最もポピュラーなケースとしては、非同期で通信を行い(NSObjectのサブクラス等)、受け取った結果を画面(UIViewControllerのサブクラス)に通知して反映する場合だと思います。大抵のサンプルもそれを前提にしていますよね?

今回やりたかったケースでは、画面と通信を行うクラスの間に通信を管理するクラスを作り、画面⇄管理⇄通信というふうに連携します。つまりNSObject→UIViewControllerと通知を投げるパターンの他、NSObject→NSObjectと通知を投げるパターンが発生するのです。

このNSObjectからNSObjectへの通知がくせ者でした。

例えば、下記のように通知を投げるとします。
NSNotification *notification = [NSNotification notificationWithName:@"DownloadComplete" object:self];
[[NSNotificationCenter defaultCenter] postNotification:notification];
通常であればこのように受取側を書くと思います。
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self selector:@selector(receiveDownload) name:@"DownloadComplete" object:nil];
ところが、この記述ではreceiveDownloadメソッドは呼ばれません。(エラーも出ません)

色々調べた結果、今回のケース(NSObject→NSObject)では受取側を下記の様に記述しなければならないようです。
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserverForName:@"DownloadComplete"
                                        object:nil
                                         queue:nil
                                    usingBlock:^(NSNotification *note) {
                                        [self receiveDownload];
                                    }];
addObserver:selector:name:object:でなくaddObserverForName:object:queue:usingBlock:の方を使う必要があったようです。
これでDownloadCompleteの通知が投げられた際に、receiveDownloadメソッドが呼ばれるようになりしました。

めでたし、めでたし。