あるオブジェクトから別のオブジェクトへイベントを通知したい場合、Delegate(protocol)かNotificationいずれかの方法で実装できます。どちらを使うかはケース・バイ・ケース。今回はその使い分け方について説明します。

※ あくまで自分の経験則によるものです。

ここでのDelegateの説明はDatasourceも含むことにします。要するにprotocolで定義したメソッドを使ってやり取りする場合です。 

Delegateを使う場合

  • 通知を受けるオブジェクト(通知先)が1つだけである
  • 通知元オブジェクトと通知先オブジェクトが緊密な関係にある
  • 通知元と通知先の間で1つ以上のやりとりがある
  • 通知元と通知先で相互的なやりとりがある

より具体的に言うと

  • 通信のように非同期処理を行い、その結果を受け取る場合
  • ViewControllerがSubviewからのイベントを受け取る場合
  • 通知先が通知元に情報を返すなど、相互に密なやり取りがある場合(Datasourceがよくこの役割を担う)

SDKではUITableViewDelegateやUITableViewDatasourceがおなじみでしょう。特にUITableViewDatasourceは、表示する項目数のやり取りなど通知元と通知先で密な連携を行います。

Notificationを使う場合

  • 通知を受け取るオブジェクトが1つ以上ある
  • 通知元オブジェクトと通知先オブジェクトの距離が遠い場合
  • 通知元からの一方的な通知である。つまり通知先からの応答を期待しない

Delegateが隣にいる人と細かいやりとりをするのに対して、Notificationは遠くの人に起きたことを一方的に伝えるイメージかもしれません。

より具体的に言うと

  • ホームボタンを押してアプリケーションがバックグランドになりサスペンド状態になったとき、複数のオブジェクトが処理すべきことがある場合。サスペンドになるという通知はNotificationが望ましい
  • 深い階層に配置されたView(あるViewのSubviewのSubviewであるなど)から、大元のSuperviewに通知を送る場合(ボタンが押されたなど)

これからをDelegateで実装しようとするとややこしくなりがちです。以下はそのややこしくなる例です。

Delegateで複数オブジェクトに対する通知に対応しようとする場合

  • 通知元がDelegateを配列で持つ必要がある
  • やり取りが不要になったタイミングで配列からDelegateを取り除く必要がある
  • 忘れるとメモリリークやクラッシュの原因になる(これはNotificationでもありえますが・・・)

いずれにせよ、Delegateは本来weakであるべきで、配列にすると通常strongになるので、その時点で危ない橋を渡ることになります。

Delegateで距離が遠いオブジェクト間の通知に対応しようとする場合

  • A > B > C > Dのような形でViewが配置されているとする。Aが大元のSuperviewでBはAのSubview、CはBのSubview、DはCのSubviewである
  • DはカスタムViewでボタンが配置されている
  • Dのボタンが押された事をAが受け取る必要がある
  • Aが直接参照できるのはBのみ。CやDは距離があるため参照できない
  • Dはボタンを押したことを通知するDelegateを用意。Cが受け取り先になる
  • Cはその通知をリレーするためにDelegateを用意。Bが受け取り先になる
  • Bはその通知をリレーするためにDelegateを用意。Aが受け取り先になる

このように無駄にDelegateが増えてしまいます。またDが通知すべきことが複数ある場合、当然Delegateメソッドが増えますが、それは一箇所だけではありません・・・。コードを書く量が増えてメンテナンス性も悪いですね。

アプリケーションの規模が大きくなるとUI周りで、こういった状況に追い込まれる事はありがちです。特に真面目に再利用可能なようにカスタムViewを作るほどに起き得ると思います。イベント通知をDelegateよりもNotificationで実装したほうが汎用的かどうかは検討の余地があるでしょう。