今日、この原稿を書こうとしている正にそのとき、Appleがサードパーティ向けiPhone SDKを提供するというニュースが飛び込んできた。

iPhoneおよびiPod touchは、ベースのOSとしてMac OS Xを採用しており、そのアプリケーション開発言語としてObjective-Cが採用されるであろう事は、容易に想像できる。スクリプティング言語はなやかなりし昨今だが、ネイティブアプリケーション開発のためのObjective-Cの活躍分野が、密かに広まりつつある事を感じる。

では、デザインパターンの話に戻ろう。Chain of Responsibilityの話も5回目になる。今回は、形成されるチェーンの話だ。

nilターゲットアクション

前回は、NSApplicationがチェーンをたぐり、そのために使われるメソッドがsendAction:to:from:であることを紹介した。このメソッドには、引数としてターゲットを指定できる。ターゲットが明確に指定されるのであれば、チェーンをたぐる必要はないのではないだろうか?

実は、sendAction:to:from:は、このターゲットの引数によって挙動が変わる。特定のターゲットが指定されている場合は、そのターゲットにのみアクションの送信を試みる。そして、ターゲットにnilが指定されている場合、responder chainを利用したターゲットの探索が始まるのだ。

この、ターゲットにnilを指定してアクションを送る事を、nilターゲットアクションと呼ぶ。

nibファイルのFirst Responder

ここで思い出してほしいのが、Interface Builderで作成するnibファイルだ。本連載の第88回で、Interface Builderとターゲット・アクションは密接な関係にあることを説明した。

nibファイルには、First Responderと呼ばれるインスタンスがある。初めてInterface Builderを起動したときは、このアイコンが何を表すか見当がつかないのだが、responder chainの考え方を紹介した今なら、理解できるだろう。

ターゲット・アクションでは、アクションの送り手からターゲットへと接続を行う。だが、responder chainを利用したいときは、ターゲットが固定ではない。そこで、First Responderへとつなぐようにするのだ。アクションをFirst Responderへと送ると、それはresponder chainの先頭へとアクションを送る事を意味する。そして、そのチェーンをたぐっていくことになるのだ。

別の見方をすると、First Responderへアクションをつなぐということは、nilターゲットアクションを行う、という事になる。

ターゲット・アクション用のResponder Chain

これで、responder chainにまつわる様々な事の説明が終わった。いよいよ、最も中心的な話題を取り上げよう。形成されるチェーンについてである。

Cocoaにおけるresponder chainでは、ウィンドウが重要になる。アクションを受け取るオブジェクトは、ウィンドウ上に配置されているGUIコントローラになるからだ。Cocoaでは、現在フォーカスがあたっているウィンドウを、キーウィンドウと呼ぶ。キーウィンドウ上にある、フォーカスのあたっているGUIコントローラ、これがresponder chainの先頭、first responderとなるのだ。

first responderからチェーンが続いていく。次のターゲットは、同じウィンドウ上にあり、ビューの階層の上位にあるコントローラだ。これを、コントローラがなくなるまで繰り返す。一番上のコントローラまで来たら、次はウィンドウがターゲットになる。NSWindowもNSResponderを継承している。

チェーンはこれで終わりではない。次は、NSWindowのデリゲートへと続く。ここで注意してほしいのは、デリゲートには任意のオブジェクトを設定できるので、NSResponderのサブクラスではないこともある。だがそれでも、responder chainに組み込まれているのだ。これは、前回説明したように、チェーンをたぐるのがNSResponderではなく、外部のクラスだからできる所行だ。デリゲートの次は、NSWindowControllerという、NSWindowに関連付けられたクラスになる。

チェーンはさらに続く。Cocoaでは、キーウィンドウの他にメインウィンドウと呼ばれるウィンドウがある。メインウィンドウは、最前面にあるドキュメントまたはアプリケーションウィンドウだ。キーウィンドウがフォントパネルの場合、メインウィンドウはアプリケーション本体のウィンドウとなる(キーウィンドウとメインウィンドウが同一のものになる場合もある)。

キーウィンドウでのチェーンが尽きると、次はメインウィンドウのチェーンに移る。先ほどと同様に、first responderから始まり、ビューの階層、NSWindow、NSWindowのデリゲート、NSWindowControllerへと続く。

次は、NSApplicationクラスだ。NSApplicationはチェーンをたぐるだけでなく、自身もresponder chainに含まれている。そして、NSApplicationのデリゲートに渡される。これでついに、チェーンが終わる。

この非常に長いチェーンが、Cocoaのアクション・ターゲットのためのresponder chainである。

このチェーンの特徴は、途中にどんなオブジェクトでも挟める点であろう。NSWindowやNSApplicationのデリゲートもチェーンに含まれるが、これらはNSResponderのサブクラスである必要がない。単に、アクションに対応するメソッドを実装しているかどうかで判定されるのである。これにより、無闇にサブクラス化を繰り返してクラス階層を無駄に複雑にする事がなくなり、すっきりとしたアプリケーション設計に寄与することになるのだ。