【コラム】

ダイナミックObjective-C

11 2つのプロトコルの使い分け

木下誠  [2005/10/19]

前回は非形式プロトコルの説明をして、デリゲートでよく使われるという話をした。今回は、形式プロトコルと非形式プロトコルの違いを追求してみよう。

非形式プロトコルに対する、形式プロトコルの利点は、次の2つにある。

  • メソッドの実装をチェックして、コンパイル時に警告を出す
  • conformsToProtocol:を使って、プロトコル準拠を調べることができる

デリゲートに、この利点が有効かどうか、説明しよう。

デリゲートの設定はInterface Builderで

Objective-Cを利用しているフレームワークであるCocoaでは、とても多くのクラスがデリゲート機能を提供している。特に、モデル・ビュー・コントローラ構造の、ビューにあたるクラスでは、基本的な動作の制御は、すべてデリゲートを通して行われる。フレームワークの設計の指針として、クラスの拡張は、できる限りデリゲートで行い、サブクラス化をなるべく避けている、ということが感じられる。アプリケーション固有のロジックは、ほとんどデリゲート内部で実装できる、ということだ。

そのように、デリゲートの利用を中心にすえているために、その接続も簡単にできるようになっている。GUI設計ツールであるInterface Builderを使って、グラフィカルに行えるのだ。デリゲートはGUIの部品に設定することが多いので、そのデザインツール上で設定するのは非常に自然だ。

対象となる部品を選択し、コントロールキーを押しながらドラッグして、デリゲートとなるオブジェクトの上でドロップする。このいつものやり方で、デリゲートも接続できる。

テーブルビューからドラッグして、delegateの設定

このように、デリゲートとなるインスタンスに接続していく。だが、上の図ではデリゲートとして、少し特殊なインスタンスを選択している。File's Ownerと呼ばれるものだ。

File's Ownerとデリゲート

Interface Builderの編集結果であるnibファイルは、File's Ownerと呼ばれるインスタンスを含む。これは、nibファイル上のオブジェクトと、外部のオブジェクトをつなぐ、窓口となるものだ。

nibファイルとは、GUIデザインを含むものだが、特別な定義フォーマットを使っている訳ではなく、ソースコードで記述している訳でもない。GUIで使われているインスタンスを、アーカイブしたものである。実行時にnibファイルを読み込むということは、アンアーカイブを行い、インスタンスを復元することである。そのときに、デリゲートを含む、インスタンス間の接続も元通りになる。

ただ、nibファイル内だけで完結せずに、外部のオブジェクトとも接続したいときもあるだろう。たとえば、デリゲートとして、アプリケーションのメインのコントローラであるオブジェクトを指定したいとき、などだ。そのようなときに、File's Ownerを使う。nibファイルを読み込むときにFile's Ownerとなるオブジェクトを指定すると、File's Ownerへの接続は、その外部のオブジェクトへの接続となる。

つまり、File's Ownerになるオブジェクトは、実行時まで分からない、ということだ。ここで、仮に、デリゲートを形式プロトコルで定義し、Interface Builder上でそのチェックを行える、としよう。だが、それは無意味である。なぜならFile's Ownerにあたるオブジェクトは、コンパイル時にもリンク時にも決定しておらず、動作させた時に初めて分かるからだ。そして、File's Ownerへのデリゲートの接続は、よく行われる。

プラグインでの形式プロトコルの利用

だが、次善の策として次のようなことは考えられる。「確かに、コンパイル時のチェックはできない。だが、File's Ownerを指定する時に、プロトコルに準拠しているかどうかをconformsToProtocol:でチェックする方法はあるのではないか」

これは、ありだろう。指定したクラスのプロトコルのチェックは、プラグインなどでよく行われる。Cocoaでは、NSBundleというクラスを使って、プラグインを読み込むことができる。その際に、読み込もうとしているプラグインが、自分が必要としているものかどうか、プロトコルを使ってチェックできる。

// プラグインのパスを指定して、バンドルの作成
NSBundle* bundle = [NSBundle bundleWithPath:pluginPath];
// バンドルから、主要クラスを取り出す
Class pluginClass = [bundle principalClass];

// 主要クラスが、MyPluginプロトコルに準拠しているかどうかチェック
if ([pluginClass conformsToProtocol:@protocol(MyPlugin)]) {
    // このプラグインを使用する
    ...
}

プラグインのように、これからファイルを読み込むとき、またはインスタンスを作成する時には、形式プロトコルによるチェックは有用だろう。

だが、nibファイルでのFile's Ownerの指定は、すでにインスタンス化されているオブジェクトを指定するので、respondsToSelector:によるチェックの方が適切だろう。なぜなら、conformsToProtocol:はクラスオブジェクトに対して適用され、respondsToSelector:はインスタンスオブジェクトを調べるからだ。

2つのプロトコルの使い分け

以上をもとに、形式プロトコルと非形式プロトコルの使い分けを考えると、次のようになるだろうか。

  • すべてのメソッドを実装する必要があり、インスタンス化をする前にチェックを行いたい場合は、形式プロトコル
  • 任意のメソッドだけ実装すればよくて、すでにインスタンス化されたオブジェクトを指定する場合は、非形式プロトコル

このように調べてみると、デリゲートのような仕組みには、非形式プロトコルの方が便利だと言えるだろう。

    新着記事

    特設サイトの情報

    人気記事

    一覧

    イチオシ記事

    新着記事

    特別企画

    一覧