iPhoneアプリ作成までの道のり12
12. NSNotificationCenter
最後にNSNotificationCenterを紹介する。プロトコルデリゲートパターンは主に1対1、KVOは1対1〜1対Nに適用するものであるが、NSNotificationCenterを使うと、N対Nのイベント伝達を疎結合に実現できる。ただし、通知センターを使う方法は、他の2つの方法よりもオーバーヘッドを伴うことになる。しかし、うまく活用すれば、ある場面では他の2つの方法よりもパフォーマンスを向上させる可能性も持っている。
N対Nとは言っても、それは別にプロトコルデリゲートパターンでも実現可能なことである。複数のプロトコルを定義して、複数のプロトコルデリゲートを所持する送信元と、複数のプロトコルを実装するデリゲートを用意すれば、それでN対Nだ。通知センターを使えば、それをもっとスマートに、疎結合に実現できるという話である。
通知センターは、N対Nが可能であること以外に2つのメリットを提供する。
①非同期処理POSTが可能
②重複する不要なメッセージの削除が可能
以下に非同期POSTの例を示す。
// NSNotificationを使った簡単なイベント伝達のサンプル static NSString* MySenderNotifyName = @"MySenderValueChanged"; // _xの値が10の倍数になったときに現在の値を通知する @interface Sender : NSObject { int _x; } @property(assign, nonatomic, readonly) int x; -(int)increment; @end @implementation Sender @synthesize x = _x; -(id)init { self = [super init]; if (self) { _x = 0; } return self; } -(int)increment { if (0 == ++ _x % 10) { NSNumber* val = [NSNumber numberWithInt:_x]; NSNotification* notification = [NSNotification notificationWithName:MySenderNotifyName object:val]; NSNotificationQueue* queue = [[NSNotificationQueue defaultQueue]; [queue enqueueNotification:notification postingStyle:NSPostNow/* NSPostWhenIdle */]]; // 非同期ポストが必要ない場合は以下の呼び出しでOK //[[NSNotificationCenter defaultCenter] postNotificationName:MySenderNotifyName object:self]; } return _x; } @end // Senderからの通知を受け取ったらコンソール出力を行う @interface Receiver : NSObject -(void)output:(NSNotification*)notification; @end @implementation Receiver -(void)output:(NSNotification*)notification { NSNumber* val = (NSNumber*)notification.object; NSLog(@"%d", val.intValue); } @end // テスト用メイン関数 int main(int argc, const char** argv) { @autoreleasepool { // ReceiverをObserverとして通知センターに登録する Receiver* receiver = [[[Receiver alloc] init] autorelease]; [[NSNotificationCenter defaultCenter] addObserver:receiver selector:@selector(output:) name:MySenderNotifyName object:nil]; // Senderに対して何か処理を行う // 条件が満たされれば、SenderからReceiverに自動的に通知が行われる Sender* sender = [[[Sender alloc] init] autorelease]; for (int i = 0; 1000 > i; ++ i) { [sender increment]; } } }
上記のコード例は意味のないマルチスレッドであるが、デバッガで追ってみれば分かる通り、完全に非同期処理が行われている。自前でスレッドを用意したわけでもないのに、イベントの送信元はメッセージをPOSTするとすぐに復帰するのだ。
ここでは詳細は割愛するが、UIにおける画面の再描画メッセージのような、大量に通知されても意味のないようなメッセージを、受信先にPOSTする前に削除、あるいはマージしてしまう仕組みも備わっている。
このように、NSNotificationCenterは今まで紹介した3つのイベント伝達の仕組みの中でも最も高度な機能を持っており、特にバックグラウンドで稼働するスレッドから、別のスレッド(主にメインスレッド)へのメッセージ通知において大きな役割を果たす。実際にiOS内部でどのように使われているのかというと、サウンド再生のイベント通知や、デバイスの向きの変更通知など、ハードウェア寄りの非同期イベント通知で使用されている。
通知センターにはこういった高度な面があるからして、やはり、最初に紹介した順、
・プロトコルデリゲート
・KVO
・NSNotificationCenter
の順に適用を検討するのが良い。