こんにちわ、 20%ルールの時間を使って30分で書きます。
Objective-C の Notification って便利ですよね。 しかし、色々な問題点を抱えています。
下のサンプルコードを例に説明します。
// 通知を受け取るオブザーバーを登録 - (instancetype)init { self = [super init]; if (self) { NSNotificationCenter *c = NSNotificationCenter.defaultCenter; [c addObserver:self selector:@selector(safe_didChangeHogehoge:) name:kHogeNotificator_safe_DidChangeHogeHogeNotification object:nil]; [c addObserver:self selector:@selector(unsafe_didFail:) name:kHogeNotificator_unsafe_HogeHogeDidFailNotification object:nil]; } return self; } // オブザーバーを登録解除 - (void)dealloc { [NSNotificationCenter.defaultCenter removeObserver:self]; } #pragma mark - 通知が来たら実行されるメソッド達 - (void)safe_didChangeHogehoge:(NSNotification *)note { // なんかする } - (void)unsafe_didFail:(NSNotification *)note { // なんかする }
Notification を使うときに発生する問題点
Notification では以下の様な問題があります。
- オブザーバーの登録が面倒
NSNotificationCenter.defaultCenter
って長い
- dealloc でオブザーバーの登録解除を忘れてクラッシュ
- セレクタの名前を変えた時に
addObserver
のセレクタ名を変更し忘れてクラッシュ - NSNotification オブジェクトの userInfo の中身がなんなのか分からない
dealloc
忘れてないかとか頑張ってチェックするしかないというのでは辛いです。
なので
Notification を Delegate に変換するパターンを使ってもろもろの問題を一気に解決します。
このパターンは臭いコードを1箇所に押し込めることによって色々な所に臭いコードが散らばらないようにするという方針で設計されています。
どうやるの?
- Notification を Delegate に変換する Dispatcher クラスを作る
- Dispatcher クラスで
addObserver
とremoveObserver
を管理する - サンプルのコードを作ったのでそちらを見て頂くほうが早いですが Dispatcher はクラスはこんな感じです
@implementation HogeNotificationDispatcher - (instancetype)initWithNotificator:(HogeNotificator *)notificatorOrNil { self = [super init]; if (self) { _notificator = notificatorOrNil; NSNotificationCenter *c = NSNotificationCenter.defaultCenter; [c addObserver:self selector:@selector(safe_didChangeHogehoge:) name:kHogeNotificator_safe_DidChangeHogeHogeNotification object:_notificator]; [c addObserver:self selector:@selector(unsafe_didFail:) name:kHogeNotificator_unsafe_HogeHogeDidFailNotification object:_notificator]; } return self; } - (void)dealloc { [NSNotificationCenter.defaultCenter removeObserver:self]; } #pragma mark - Notification - (void)safe_didChangeHogehoge:(NSNotification *)note { NSString *hogehoge = note.userInfo[kHogeNotificatorHogeHogeUserInfoKey]; [self.delegate safe_dispatcher:self didChangeHogeHoge:hogehoge]; } - (void)unsafe_didFail:(NSNotification *)note { NSError *error = note.userInfo[kHogeNotificatorErrorUserInfoKey]; [self.delegate unsafe_dispatcher:self didFail:error]; } @end
利点
- Dispatcher クラスの
dealloc
でremoveObserver
されていれば他の場所で考える必要がなくなる - ViewController はテストしにくいけど Dispatcher クラス単体ならテスト簡単 (OCMock とか使う)
- どんな通知が来るか分からなくても Delegate が実装されてないと警告が出るので思考停止してても実装できる
- userInfo は何が入ってるかよくわからないけど、 Delegate のメソッドの引数になってれば何が入ってるか一目瞭然
note.userInfo[kHogeHogeUserInfoKey]: // 何が入ってるかわからん
- (void)dispatcher:(Dispatcher *)dispatcher didChangeHogehoge:(NSString *)hogehoge { // NSString の hogehgoe ってやつが入ってることが分かる }
欠点
- コード増える
- 最初コードを増えるだけで通知を受け取る場所が増えればコードの総量は減ります
サンプルコード
ってなかんじです。
13:00 から書き始めて 13:29 に書き終わり!!!!