Hatena::Groupsubtech

ういはるかぜの化学

Saturday, February 14, 2009

Visual Studio ユーザがはじめての Cocoa + Objective-C + Xcode 00:52 Visual Studio ユーザがはじめての Cocoa + Objective-C + Xcode - ういはるかぜの化学 を含むブックマーク はてなブックマーク - Visual Studio ユーザがはじめての Cocoa + Objective-C + Xcode - ういはるかぜの化学

あってるかどうかはわからないのですが、何となくいつになっても理解してない部分があったので調べつつふんふんと理解をまとめてみるテストです。まあ間違ってたらきっとDISられますがこだわりません。

というか慣れた後だとこういう何がよくわからなかったのか視点が絶対できなくなってしまうので書けるうちに。

いやべつにVisual Studioユーザに対しての、というわけじゃないですよ!ないです!主に Interface Builder を開いて ?でわっしょい! 状態になるのを解決したかったのです。

スタートアップ

  • main (main.m) が呼び出されます
    • NSApplicationMain (参考: Cocoa Fundamental Guide: 始める前に )
      • アプリケーションオブジェクト作成
      • NSAutoreleasePool のセットアップします
      • 初期インターフェースをnib(xib)ファイルから読み込みます
        • ここで読み込むファイルはinfo.plistにある ”Main nib file base name" を読んで決定しています。普通にCocoa アプリケーションを作るとデフォルトでMainMenu.xibです
        • nib(xib)ファイルを読み込んだらInterface Buildernib(xib)を開いたときにでてくる一覧(Xcode 2のころのIBであればInstances タブ)にあるものを全てインスタンス化します
        • インスタンス化したオブジェクトのアウトレットやアクションをくっつけます
      • アプリケーションを実行
        • (アプリケーションイベントループ)

ということは自分のコードはどうすると呼ばれるの?

Windows Forms で作る場合、Visual Studioさんが標準で用意する線は Program クラスの Main メソッド内で自分の実装を含む Form 継承クラスがインスタンス化され、Application.Run に渡して Form クラスががんばってくれます。そしてイベントが来たらその Form のメソッドが呼ばれる。となっています。

一方 Cocoa では nib(xib) にある、オブジェクトがインスタンス化されて各種イベントハンドラのようなものや参照みたいなものの接続をしてくれます。そしてイベントが来たら接続されたオブジェクトのメソッドが呼ばれるという形になります。

ですので Windows Forms だと Form クラスを継承していてコントローラと一蓮托生状態ですが、Cocoa ではコントローラがなくてもうごきますというか、コントローラはInterface Builderで起動時に「インスタンス化するべきオブジェクト」として追加する必要があるわけですね。そしてそのオブジェクトにつないでもらっていろいろ受け取ったりとやり取りするわけです。言い方を変えるとオブジェクトにわざわざつながないとイベントでなにかは呼ばれません。

普段 Visual Studio を使っていて Xcode + Interface Builder を使ったときに混乱するのが Visual Studio だと Form1.cs が用意されているのに、Xcode + Interface Builder ではそれに相当するものがないからですね。Window がありますがこれは Form1.Designer.cs に相当するものだということです。

Form1.cs に相当する何か処理をこなす AppController などの任意のコントローラクラスを用意してあげて、しかもコントローラオブジェクトのインスタンスとして存在する必要があるのです。

オブジェクトの接続

Cocoaではアウトレット、アクション、デリゲートといったオブジェクトとの接続でオブジェクト参照、イベントハンドリングなどを行います。

アウトレットもアクションもInterface Builderだとコントローラなどと結びつけるとき、このコントローラにはこれがつながると見えるのがVisual Studio使い的にはよいかもしれません。

アウトレット

コントローラ”から”UIオブジェクトを参照するための接続です。

つまりAppControllerに IBOutlet id button1; と宣言し、AppControllerからボタンへ向かってInterface Builderで接続するとコントローラ内で参照できるということです。

f:id:mayuki:20090215002532p:image

.NETでいうところのコードビハインドが何となくにてるかもしれません。

イベント (アクション)

UIオブジェクトで何かイベントが発生したときにUIオブジェクト”から”コントローラにむかってメッセージが送られる接続です。あらかじめ結びつけておいた「指定したターゲットの指定したメソッド」を呼び出します。

いわゆるイベントハンドラです。

たとえばボタンを押したときAppControllerのbutton1Pushed:が呼ばれたいとすると、Interface BuilderでボタンのアクションとしてAppControllerのbutton1Pushed:をくっつけておいてあげます。

f:id:mayuki:20090215003139p:image

Interface BuilderのそれはWindows FormsなどのイベントハンドラのVisual Studioでの扱い方に似ています。実際の仕組みは結構違いますが、コントローラ(ターゲット)がFormでセレクタが適切なデリゲート(C#的な)だと思えば何となく。

イベント (Cocoa的な意味でのデリゲート)

アクションではないイベント、たとえばデータが表示された、ウィンドウなどが閉じられるなどイベントはデリゲートという仕組みで受け取ります。デリゲートはそのハンドリングしたいイベントを持つオブジェクトの delegate にコントローラを指定するという形で接続します。

f:id:mayuki:20090215005014p:image

delegate が設定されていると何らかのイベントがあったときコントローラにメッセージを送ろうとし、ハンドリングできそうであればやってもらうという風な仕組みです。やれそうであればやってもらうというあたりがデリゲート(委譲)ってことなのかもしれません(てきとう。

つまり Window の delegate として AppController を指定しておくと、Window が閉じられるとき AppController に対して windowWillClose: を呼べそうなら呼び出します。

無理矢理Windows Formsで同じようなことと言うとControlのOnほにゃらふにゃらをoverrideする感じでしょうか。まあ全然違いますけど。

CocoaではいろんなUIオブジェクトがdelegateを持っていてある、プロトコルに適合していたらふにゃらほにゃらとかよくある話っぽいです。

アウトレット、アクション、デリゲートくっつけかた

今回のこの説明のスクリーンショットでは各オブジェクトを右クリック(control+クリック)するとでてくるHUDなパネルで接続しましたが、対象から右ドラッグ(control+ドラッグ)でも接続できます。

個人的にはこのHUDのパネルの方が「このオブジェクトには何がつながるのか」が一目で分かるのでいいかもと思います。ドラッグだとOutletの場合には「それに対して何をどこへくっつけるか」なのに、アクションは「何にそれに対してのアクションをくっつけるか」なのでちょっと混乱します。

謎のFile's OwnerとFirst Responder

Interface Builderで開く気になるアイツ、File's OwnerとFirst Responder。なじみのない言葉なので何となくわかるようなわかんないような。

File's Owner

これはその名の通りというかそのnibを管理しているクラスのインスタンス。だからオブジェクトアイコンなのですね。

例えばデフォルトな MainMenu.nib の場合 NSApplication になるから、NSApplication の delegate をコントローラと接続したいときに使ったりできるとかなんとか。

First Responder

Cocoa には Responder Chain (Cocoa Event-Handling Guide: レスポンダとレスポンダチェーン) というまあイベントバブリングみたいなそれがあって、それの一番最初に応答するやつということになるみたい。

なので要するに何か特定のアレというわけではなく「フォーカスが当たっているオブジェクト」だということですね。

よくわからないのですが例えばデフォルトだと MainMenu の Menu Item (About NewApplication) の Sent Actions にある orderFrontStandardAboutPanel: が First Responder に結びついているのは、メニューが選択されたらとりあえず First Responder (選択されているオブジェクト)に投げられ、どんどんたらい回しにされるも途中誰も何もしないので Chain のどこかにいる NSApplication が処理して「アプリケーション について」のダイアログがでると。

トラックバック - http://subtech.g.hatena.ne.jp/mayuki/20090214