第4章 MyClip プロジェクト | ||
ホーム | メール | |
目次 | < 前ページ 次ページ > |
今度はMyClipにControllerクラスを追加しましょう。次の手順にしたがって作業をおこなってください。
【Xcode 3.1の場合】
前説を参考にしてControllerクラスをClassesフォルダに作成してください。
Controller.hを選択するか、もしくはダブルクリックしてコードを表示させてください。そして次のように強調箇所のコードを自動で作成されたヒナ型に追加します。
#import <Cocoa/Cocoa.h> #import "Model.h" @interface Controller : NSObject { Model *model; IBOutlet NSTextView *textView; } @end
#import <Cocoa/Cocoa.h>
Modelクラスのヘッダファイルの項でも説明したとおりです。Cocoa.hを読み込むことによってFoundation.hとAppKit.hというCocoaフレームワークで良く使われる多くのクラスのヘッダファイルをまとめて読み込むことができます。
#import “Model.h”
前節で作成したModelクラスのヘッダファイルを読み込んでいます。これによりこのController.hのなかでModelクラスが使えるようになります。
Cocoa.hはすでにModel.hで読み込んでいます。そしてそのModel.hをこのController.hは読み込んでいます。つまりこのControllerクラスではCocoa.hを読み込む必要はないわけです。読み込む必要がないだけでなく、これがC言語のincludeプリプロセッサで読み込んだ場合はCocoa.hで定義されている関数などが2重定義されていることになり、コンパイル時エラーになります。importプリプロセッサが2重読み込みをおこなわないというのはこのことを予防しているということです。なお、今回のControllerクラスファイルはヒナ型を利用して作成したものです。したがって自動的にCocoa.hをimportするプリプロセスコードが書かれてしまうことになります。
Model *model;
インスタンス変数としてModelクラス型のポインタであるmodelを宣言しています。この変数にModelクラスのインスタンスのポインタを格納してControllerオブジェクトからModelオブジェクトを扱えるようにします。また、何度か説明しているとおり、Objective-Cにはすべてのクラスのオブジェクトに使えるidという型があります。このid型を使って前述のコードを次のようにすることもできます。
id model;
なお、このid型は最初からポインタになっていますので *(アスタリスク)を付ける必要はありません。便利な型ではありますが、最近では必要な場合をのぞいてid型の使用は控えるようにとApple社からの指針が出ていることはすでに説明しました。その理由を再確認すると次のようになります。
IBOutlet NSTextView *textView;
textViewというインスタンス変数は後ほどこのMyClipに搭載する“NSTextView”というビューパーツを参照するためのものです。先頭の“I Outlet”はInterface Builderからこのインスタンス変数の存在が見えるようにするための文字列です。このIBOutletという文字列が書かれていない場合はInterface Builderからこのインスタンス変数の存在は見えません。そしてこのI Outletという文字列はコンパイルに先立つプリプロセス時に削除されることになっています(空文字に置換されます)。
Viewオブジェクトは通常多数が使用されます。次節ではViewオブジェクトをInterface Builderを使って作成していきますが(作成しますが)、そのなかでNSTextViewというCocoaフレームワークで用意されているビューパーツを中心にしてViewオブジェクトを作成していきます。そしてViewオブジェクトからControllerオブジェクトを介してModelオブジェクトのデータを変更したり、あるいはModelオブジェクトのデータを、Contorollerオブジェクトを介してViewオブジェクトに表示したりします。
なおこのNSTextViewのヘッダファイルはCocoa.hのなかに含まれています。
Controller.mを表示してください。そして次のように強調箇所のコードを自動で作成されたヒナ型に追加します。
#import "Controller.h" @implementation Controller - (id)init { self = [super init]; if (self) { model = [[Model alloc] init]; } return self; } - (void)dealloc { [model release]; [super dealloc]; } @end
#import “Controller.h”
Controller.hを読み込むことによって、Controller.hで読み込んだCocoa.hとModel.hも読み込んだことになります。
- (id)init - (void)dealloc
この2つのメソッドはヘッダファイルでメソッド宣言をしていませんでした。この init メソッドと dealloc メソッドはスーパークラスである NSObjectの中で宣言・定義されているメソッドです。「クラスの継承」のところで説明したようにスーパークラスで宣言・定義されているすべてのメソッドはサブクラスに継承されますが、それらのメソッド宣言をサブクラスであらためて記述する必要はありません。そしてメソッド定義も変更する必要がなければあらためて記述する必要はありません。継承したメソッドについては実装を変更する場合、すなわち上書きをする場合だけメソッド定義を記述しなおします。しなおすことになります。
このController.mでは-initメソッドと-deallocメソッドを上書きしています。この2つのメソッドは今後もみなさんが最も上書きすることになるであろう代表的なメソッドです。
<▽コメント>ここでのinitメソッドの説明は第3章とダブっていますが、ここも一旦このままで掲載します。<△コメント>
この段落でのinitメソッドの説明は第3章での説明の繰り返しになっています。しかし頭の整理のために敢えて説明を繰り返すことにいたしました。また今回はsuperとselfというキーワードについて前回よりも説明を加えています。今回はこのsuperとselfに焦点をあてて説明したいと思います。
-initメソッドではsuperに対してinitメッセージを送ってその結果をselfに代入しています。このsuperはスーパークラスを表し、selfはそのメソッドを実行しているオブジェクト自身を表します。ともにメソッド内だけで使える特別な言葉です。そしてinitはインスタンス変数の初期化を行うメソッドです。ここではまずスーパークラスをルートクラスまでさかのぼり、ルートクラスから順次、インスタン変数の初期化を行いながら次のサブクラスへ、そしてその次のサブクラスへと各クラスのインスタン変数の初期化を行っていきます。そしてinitメッセージを送信したオブジェクトの直前のスーパークラスまでの初期化に成功するとselfに自分自身を表すポインタを代入します。直前のスーパークラスやルートクラスのポインタが代入されるわけではありません。ルートクラスにいたるどこかのスーパークラスで初期化に失敗した場合にはselfにはnilが代入されます。nilは「存在しない」、もしくは「偽」という意味になります。この-initメソッドの定義は理屈で考えると分かりにくいコードになっています。
次のinitメソッドの実装コードは一種の慣用句だと思ってこのまま覚えてもらったほうが良いでしょう。
self = [super init]; if (self) { /* そのオブジェクト固有の初期化式 */ } return self;
なお最初の2行は、次のようにまとめることができます。
if ( self = [super init] ) {
NSObject Class Referenceを見ていただくと分かりますが、-initメソッドの戻り値はid型になっています。これは当然のことです。initメソッドはいろいろなクラスで使われます。したがって特定のクラスに依存した戻り値にすることはできないわけです。このようにid型は便利な存在でもあるわけです。なおselfは変数に代入したりメソッドの戻り値として使うことができますが、superは変数に代入したり戻り値として使うことはできません。あくまでもスーパークラスにメッセージを送る時の、メッセージ式のレシーバとして使うことができるだけです。このsuperとselfについて次にまとめます。
【superとselfのまとめ】
では次にこのControllerクラスのinitメソッドがおこなう固有の初期化式を見てみましょう。ここが上書き(追加)しているコードということになります。
model = [[Model alloc] init];
この式の概要についてはすでに「ランタイムとメッセージ式」のところで説明していますが、もう一度繰り返します。このネスト(入れ子)されたメッセージ式は次の2つのメッセージ式に分けることができます。
[Model alloc];
このメッセージ式でModelクラスのインスタン変数がメモリに確保されます。そしてインスタンスとしての自分自身のアドレスを返します。
model = [インスタンス init];
前述のメッセージ式で返されたインスタンスに対してinitメソッドを送って初期化を行います。そして今度は初期化の終わった同じインスタンスのアドレスを戻り値としてmodel変数に代入しています。
前述のControllerクラスのinitメソッドではModelクラスのインスタンスを作成しています。あるクラスのインスタンスをそのクラスが自分自身で作ることは出来ません。今回の場合はControllerクラスからModelクラスのインスタンスを作っています。もっと正確に言えばControllerクラスのインスタンスが初期化される時にinitメソッドのなかでModelクラスのインスタンスを作っています。コントローラオブジェクトの役割は
という2点に集約されますが、モデルオブジェクトの生成も慣習的にコントローラの役割になります。「モデル・データのファイル入出力」がコントローラの役目とすれば、そのモデルオブジェクトの生成にも責任を持つと考えるのが当然と言えるでしょう。
オブジェクトはクラスという設計図をもとにしてallocというメソッドでメモリにインスタンス変数の領域を確保しinitメソッドで初期化して使いはじめます。そしてオブジェクトが不要になった場合はdeallocメソッドで確保していたメモリ領域を破棄します。deallocメソッドのコーディングはinitメソッドの逆になります。まず自分自身の後始末をしてからスーパークラスのdeallocメソッドを順次呼び出していきます。
- (void)dealloc { [model release]; [super dealloc]; }
[model release]で使われているreleaseメソッドについては前章でも説明いたしましたがレシーバの参照カウントの値をひとつ減らすメソッドです。このControllerオブジェクトにおいてmodelオブジェクトの参照カウントの値が2以上になることはありません。したがってreleaseメッセージを受け取ったmodelオブジェクトの参照カウントの値は0になります。そして参照カウントの値が0になったオブジェクトはそのオブジェクトのdeallocメソッドによって破棄されてオブジェクトが占有していたメモリ領域は解放されることになります。
なお、Objective-C のバージョン2.0からはガベージコレクションと呼ばれる、メモリ管理についてのコーディングを一切しなくてもよい方式も採用になったとことは前章でも述べたとおりです。
詳しくは6章で説明いたします。
以上でModelクラスにメソッド宣言とメソッド定義が、Controllerクラスにメソッド定義がコーディングされました。前節で「後ほど説明いたします」と言っていたプロジェクトウィンドウの「右プルダウンメニュー」に付いて説明します。
図 右プルダウン1
プルダウンメニューからメソッドを選ぶと、そのメソッドのシグネチャが選択された状態でエディタに表示されて便利です。また、メソッド以外にも@interfaceや@implementationもプルダウンメニューから選択できますが、こちらはあまり有用ではないでしょう。
図 右プルダウン2
今はまだModelクラスもControllerクラスも宣言もしくは定義しているメソッドは2つだけです。このような段階でメソッドを見つけることに苦労することはないないと思いますが、やがてメソッドが増えてきた場合にはこのプルダウンメニューを使って該当メソッドをエディタに表示させる方法は、メソッドを探す手間が省けてとても便利です。
目次 | < 前ページ 次ページ > |