前回は、File's Ownerアイコンを調査しましたが、今回は、もうひとつの謎のアイコンFirst Responderの仕組みを調べてみたいと思います。こちらも、File's Owner同様「線を引く相手がいない!」「さてどうする?」と言う問題点が謎を解くヒントとなります。
First Responderアイコンの利用法を見るには、テンプレートから作成した「Cocoa Document-based Application」に含まれるMainMenu.nibファイルのMainMenuオブジェクトをオープン(表示)します。例えば、そのEditメニューのCutを選んで、InspectorのConnectionsタブでTarget/Actionを調べてみると、グラフィックの接続ラインはFirst Responderアイコンに伸びており、それをTargetとする「cut:」アクションが選ばれていることが分かります。また、FileメニューのNewも同じく接続ラインはFirst Responderアイコンに伸びており、こちらは「newDocument:」アクションが選ばれています。
何らかのアプリケーションを作成すると分かるのですが、EditメニューのCutの対象となるターゲット・オブジェクトを先んじて固定することはできません(可能な場合もありますが...)。Cutの対象は、ある時はテキストかもしれませんし、またある時は画像かもしれません。テキストであっても、入力フィールドが複数ある場合には状況によって対象が変わります。つまり、Interface BuilderでTarget/Actionを設定しようとしても、Targetとなるオブジェクトを先んじて選べないのです。FileメニューのNewについても同じ事が言えます。
そのため、こうした場合の接続先としてFirst Responderアイコンが用意されているわけです。Interface BuilderのMainMenu.nibウィンドウからClassesタブを選んでクラスブラウザを表示させると、NSObjectを継承した位置にFirstResponderが用意されています。ここでFirstResponderを選択し、InspectorのAttributesタブを見てみると、ディフォルトで81のアクションが定義されていることが分かります。こうして用意されているアクションは、 使いなれたCarbonの仕組みで言うならば、Carbon Eventにディフォルトで用意されている'cut 'や'new 'といったコマンド(OSType)と同じだと考えれば良いでしょう。
Target/ActionのActionの方はInspectorで指定できたのですが、Targetの中身の方はどうなっているのでしょうか?TargetとしてFirstResponderが接続された場合、その中身は「nil」つまり不定となります。Cocoaの仕組みでは、このnilターゲットを持ったアックション・メッセージは、それに応答できるオブジェクトに遭遇するまで、順次ターゲットを変えながら伝搬されます。最初にどのオブジェクトにメッセージを渡し、ダメなら次はどれを選ぶのかと言う伝搬順序には正式なルールがあり、最終的には正しいオブジェクトで正しい処理がなされることになります。つまり、nilターゲットへ渡したいアクションは、すべてFirst Responderアイコンに接続しておけば良いわけです。
この仕組み、Carbon使いの人であれば、Carbon Event Handlerでのイベント処理方法と似ていることが理解できるでしょう。例えば、メニューやボタンからのコマンドを受け渡すkEventClassCommandクラスのkEventProcessCommandイベントは、最初はフォーカスされたコントロールのHandlerに渡され、そこで処理されないとウィンドウのHandlerへ、そこでもダメならアップリケーションのHandlerへと伝搬されます。 イベントの伝搬を止めるには、こうした階層のどこかのEvent Handlerの返値(OSStatus)をnoErrとします。つまり、処理が完了した時点でnoErrを返せばイベント(メッセージ)の伝搬を止められるわけですが、その代わりにeventNotHandledErrを返せば、イベントは次の階層(クラス)へと伝搬されます。
さて、今回も名称に対する不満なのですが(笑)、このFirst Responderというアイコン名称はどうも気に入りません。Cocoaでは、ウィンドウ上のアクティブビュー(現在テキスト入力のターゲットとなっているビューなど)を指示すために用意されているOutletを「FirstResponder」と呼ぶため、いくらか混乱を生じているような気がします(筆者も混乱した)。こちらは、Carbonで言うところのフォーカスされたコントロールのことですね。ですから、First Responderではなく、もう少し分かり易い名称は無いものでしょうか?ダイレクトにNil-Target-Objectなどでも良いような気がします...。
ところで、MainMenu.nibというファイル名にもしっくりきてない人はいませんか?Carbonプロジェクトのmain.nibでも良いのですが、Application.nibとかの方がピッタリきます。ちなみに、この名称を変更することは可能です。ただし、ファイル名を変更しプロジェクトファイルに再登録しただけでは、アプリケーション起動時に読み込んでくれません。プロジェクトのターゲット(アプリ名アイコン)をダブルクリックして表示される「ターゲットの情報」ダイアログの「プロパティ」タブにおいて「主要Nibファイル」の内容をMainMenuからApplicationへ変更しておく必要があります。
ついでに、MyDocument.nibの方もMainDocument.nib変更してみます。まず、Interface BuilderのClassesタグで、MyDocumentクラスをMainDocumentに変更し、同時にFile's OwnerのCustum ClassもMainDocumentにします(ここは自動にそうなります)。そしてMainDocument.hとMainDocument.mのソースファイル名も変更し再登録し、中身のクラス記述もすべてMyDocumentからMainDocumentへ置換してしまいます。忘れてはいけないのは、 MainDocument.mのwindowNibNameメソッド内の「return @"MyDocument"」を「return @"MainDocument"」に変更しておくことです。そして最後に、ターゲット情報のプロパティタブの「書類のタイプ」の「クラス」をMainDocumentに直せば完了です。
Leopard(Mac OS X 10.5)の販売開始日が正式に発表されました。同時に、Apple社からはiPhone用SDK提供の発表もありましたので、Objective-C+Cocoaによる開発環境もいよいよ面白くなってきました。 本連載でテンプレート・プロジェクトに含まれているNibファルなどをちまちま解説して時間稼ぎをしていた理由は、実はLeopardでCocoaの開発環境が大幅に変更されることが分かっていたからです。つまり、先んじて説明したことが無駄にならないように考えた苦肉の策なのです(笑)。やれやれ、やっとその呪縛から解放される時が来たようです。
次回からは実施にObjective-Cのソースコードを記述することで、テンプレートから作成した「Cocoa Application」と「Cocoa Document-based Application」を拡張していく作業に入ります。新着のLeopardにて開発作業を開始いたしましょう。