Objective-Cでの自前のイベントっぽい仕組み
移転しました →
非同期でネットワークからデータを取ってくる系の処理が終わったときにUITableView
のreloadData
を呼びたいみたいなとき。どうやってやるのがいいんでしょうか?
- delegate
- observerパターン
- KVO
- ReactiveCocoa
- 自前でなんか作ったやつ
iOSでよくあるのはdelegateなんでしょうが、非同期処理をするクラスとUITableView
を持ってるUIViewController
が結構遠いのでdelegateにすると引きずり回すことになってあまりよくない気がしました。この方法って片方がもう片方を直接参照できるときじゃないと使いにくいですよね?
KVOとかReactiveCocoaもそんな感じがしました。
自前で作ったやつ
なんかもやもやしたのでとりあえず自分で作ってみました。これ全然ダメじゃんみたいなことはすごくありそうなのでそういうときは誰か突っ込んでくれると嬉しい。
実装
EventManager
みたいな登録と呼出の口がついているシングルトンを用意しました。
@interface EventManager : NSObject + (id)sharedInstance; - (void)add:(NSString *)key target:(id)target selector:(SEL)sel; - (void)call:(NSString *)key args:(NSArray *)args; @end☄
実装はこうなっています。
- (void)add:(NSString *)key target:(id)target selector:(SEL)sel [self.targets setObject:target forKey:key]; [self.selectors setObject:NSStringFromSelector(sel) forKey:key]; } - (void)call:(NSString *)key args:(NSArray *)args { id target = [self.targets valueForKey:key]; SEL selector = NSSelectorFromString([self.selectors valueForKey:key]); [target performSelector:selector withObject:args]; }☄
エラー処理とかそういうのは勘弁してください。
使い方
冒頭に述べたようなケース、非同期処理が終わったら遠いところにいるUITableView
の再描画ってやつの例です。
まず登録のほう。
- (void)viewDidLoad { [[EventManager sharedInstance] add:@"reload" target:self selector:@selector(reloadTable)]; } - (void)reloadTable { [self.tableView reloadData]; }
続いて呼び出し。
- (void)hogeDidFinish:(id)sender { [[EventManager sharedInstance] call:@"reload" args:nil ]; }☄
お互いが直接知らなくてもEventManager
と文字列を介してなんとかreloadData
にまで辿り着くことができました。
Mediatorパターン?
EventManager
は直接知らない人同士のやりとりを仲介するためのクラスでした。ってか、これってMediatorパターン
になるんですかね。
書いていて気がついたのですがdelegateを使う場合だと仲介者がいれば参照できなくても書けそうですね。今回の場合は文字列を使ったのでよりなんでも有りな書き方ができてしまいそうですが、delegateを使うとなるとプロトコルをいくつも用意することになってイマイチかも。
Notification Centerに気づく
↑まで書いたあとでNSNotificationCenter
の存在を知る。いやいや、その名前じゃiPhoneで見るあの通知画面のことかと思っていたよ。ということで使い方を見てみたらさっきのEventManger
とほぼ一緒でした。
それなら標準のほうがロバストだろうし自作することはありませんね。さようならEventManager
。。。