第11回iPhone輪講
CHAPTER04
CHAPTER04
オブジェクトの型と
動的結合
動的結合とは
実際のプログラミングでは様々なクラスのインスタンス
を使う事になる
‣ すべてのオブジェクトがid型で表されていると
「どのオブジェクトがどのクラスのインスタンス」か
わからなくなりやすい
動的結合とは
List4-1
動的結合とは
動的結合
このプログラムは端末から入力された数により
変数objに代入するオブジェクトのクラスが異なるよう
になっている
クラスA,BはwhoAreYouというメッセージに対応
できる
0を入力すると「I m A」が表示される
1を入力すると「I m B」が表示される
動的結合
クラスNSObjectではwhoAreYouを定義していない
ため対応できない
2を入力するとエラーが発生する
‣ しかしコンパイルできてしまう
動的結合
ObjCは『あるオブジェクトで,そのメッセージが処理
できるかどうか,どのように処理されるか』は,
オブジェクトへ実際にメッセージを送ったときに
決定される
動的結合
すべてのインスタンスは自分がどのクラスなのか
知っている
メッセージを受け取ったときにそれに応じた処理が
実行できる
逆にそのオブジェクトが受け取ったメッセージを
処理できないときにエラーが返る
動的結合
送られてきたメッセージに対応してどのメソッドが
実行されるのかが実行時に決定される方式を
動的結合(dynamic binding)あるいは,動的束縛と
呼ぶ
ポリモルフィズム
List4-1では変数objに代入されているインスタンスが
クラスA,クラスBどちらのものでも同じメッセージに
反応した
だが,その反応は代入されているインスタンスに
依存していた
ポリモルフィズム
同じメッセージを送っても,レシーバのオブジェクトに
応じて適切なメソッドが選ばれて実行される性質を
ポリモルフィズム(polymorphism)と呼ばれる
型としてのクラス
クラス名を型として使う
定義したクラスは新しい型として使える
オブジェクトは基本的にid型で表す
しかし特定のクラスのインスタンスであることを
明示する事もできる
クラス名を型として使う
あるクラスFooのインスタンスであることを
明示するには
という型を用いる事が出来る
この型はオブジェクとが代入される変数,
メソッドや関数の引数,返り値の型として
普通に使える
Foo *
クラス名を型として使う
例えば変数の宣言は,
これは「オブジェクトはポインタとして
表現されている」ということ
Volume *v;
MuteVolume *mute;
クラス名を型として使う
「オブジェクトがポインタとして表現さている」
代入によって変数v2はv1が指すものと
同じものを指す
printf()で出力される値は0でなく1になる
Volume *v1, *v2;
v1 = [[Volume alloc] initWithMin:0 max:10 step:1];
v2 = v1;
[v1 up];
printf(“%dn”, [v2 Value];
nil
空のポインタnil
ObjCでは何のオブジェクトも表していない
(ポインタが実体を何も指していない)ことを
表すのにnilという値を用意している
nil
nilはid型のポインタで値は0
イニシャライザが初期化に失敗した時に返す値はnil
インスタンスオブジェクトを新しく作成するとき
メソッドallocはインスタンス変数を0で初期化する
id型やその他オブジェクトへのポインタもnilで
初期化する
型の静的なチェック
オブジェクトをid型としてでなく,クラス名を明示して
宣言する
するとコンパイル時に型チェックが行える
構文上宣言したクラスと,そこに使われる
オブジェクトのクラスが一致しない場合警告を
表示する事が出来る
型の静的なチェック
ObjCで使われるクラスが確定している場合
クラス名を明示して型宣言を行う事によって
メッセージが処理可能かどうかをコンパイル時に
チェックできる
List4-2
型チェックの実験
List4-2
型チェックの実験
型の静的なチェック
このプログラムはクラスAとCは独立して定義され,
BはAのサブクラスとして定義されている
main関数の中で変数をクラス名を使って定義している
型の静的なチェック
コンパイルすると以下の警告が表示される
クラスCにはwhoAreYouメソッドが無い為
表示されている
型チェックは機能している
型の静的なチェック
main関数の中の変数の型を次のように変更し,
コンパイルする
警告は出なくなるが,実行するとエラーが返る
C *c; id c;
型の静的なチェック
メソッドの呼び出し部分を以下のように変更して
コンパイルする
これで実行できるようになる
実行結果は
[a whoAreYou];
[b whoAreYou];
[c whoAreYou];
[a whoAreYou];
[b whoAreYou];
[c printName];
型の静的なチェック
変数aもbもクラスAのインスタンスのとして型が
宣言されている
しかし特に警告が出ない
クラスAから継承した機能だけ使っている限り
クラスBのインスタンスも「Aのインスタンスだ」
と言う顔をして動作させる
型の静的なチェック
次に変数bのメソッド呼び出しを以下のようにする
という警告が出るが,問題なく動作する
[b whoAreYou]; [b sayHello];
型の静的なチェック
警告を出さないようにするには素直に変数bを
クラスBのインスタンスとして宣言すればよいが,
以下のような方法もある
変数bをクラスBのインスタンスとしてキャストし,
コンパイラをごまかしている
[(B*)b sayHello];
型の静的なチェック
最後に変数aはクラスAのインスタンスだが,
以下のようにキャストしたらどうなるか
実行すると「I m A」と出力される
実行されるメソッドがキャストによって静的に
決まるということはない
[(B*)a whoAreYou];
プログラミングにおける
型宣言
シグネチャが異なる場合
メッセージセレクタには引数や返り値の型の情報はな
いが,セレクタと型情報を合わせたものを
シグネチャ(signature)と呼ぶ
インタフェースでメソッド宣言に用いる以下のような
形式のことをシグネチャ呼ぶ事がある
- (id)cellAtRow:(int)row column:(int)col;
プログラミングにおける
型宣言
プログラム内のメソッド呼び出しには
メッセージセレクタが書かれているだけで,
引数や返り値の型まではわからない
セレクタが同じでも引数や返り値の型が異なる
メソッドがあった時はどうなるのか
プログラミングにおける
型宣言
次のインタフェースが宣言されていたとする
@interface X : NSOBject
- (int)value;
@end
@interface Y : NSObject
- (float)value;
@end
@interface Z : X
- (const char *)value;
@end
プログラミングにおける
型宣言
このとき以下のメッセージはコンパイルできてしまう
実際どんな値が返されるコードが生成されるのかは,
コンパイラ任せになってしまう
id obj;
...
a = [obj value];
プログラミングにおける
型宣言
以下のような場合どうなるか
この場合変数barは期待通りの結果が得られる
変数fooにはクラスZのインスタンスが代入されてい
る可能性がある
実行時に誤作動するかもしれない
X *foo;
Y *bar;
...
a = [foo value];
b = [bar value];
プログラミングにおける
型宣言
レシーバや引数となるオブジェクトの型が実行時に
決まってもよいとする以上,メッセージのシグネチャが
何通りも存在していたのでは正しくコンパイルできない
プログラミングにおける
型宣言
結局,ObjCにおいては基本的に,
『同じセレクタを持つメッセージは引数や返り値の型
(シグネチャ)を同じにすべきである』
と,結論づける事が出来る
クラスの前方宣言
あるクラスのインタフェース部を記述する際
インスタンス変数の型やメソッドの返り値,
引数の型として別のクラスを指定することがある
このような場合その型名がクラスである事を
どうやってコンパイラに知らせるのか?
クラスの前方宣言
1つの方法は,そのクラスのインタフェース部を含む
ヘッダファイルをインポートする
#import <Foundation/NSObject.h>
#import “Volume.h” //Volumeクラスのインタフェース部をインポート
@interface AudioPlayer : NSObject {
Volume *theVolume;
...
}
- (Volume *)volume;
...
クラスの前方宣言
ヘッダファイルにはクラス名の定義以外の様々な情報が
書かれていることもある
コンパイル時に必要以上に処理が重くなる事も
考えられる
クラス名を型として使うだけなら,「クラス名である」
という以上の情報は必要ない
クラスの前方宣言
以下のような書き方が出来る
@classというコンパイラ指示子で
「Volumeという識別子はクラス名である」という
事を宣言している
これをクラス名の前方宣言(forward declaration)
と呼ぶ
#import <Foundation/NSObject.h>
@class Volume; //Volumeというクラスを使う事を宣言
@interface AudioPlayer : NSObject
...
クラスの前方宣言
@classの後には「,」で区切って複数個書く事が出来る
末尾には「;」を置く
@classは何個でも書く事が出来る
@class NSString, NSArray, NSMutableArray;
@class Volume, MuteVolume
インスタンス変数の
情報隠蔽
インスタンス変数へのアクセス
これまでのプログラムの例ではインスタンス変数に
アクセスできるのは保持しているオブジェクト自体
ObjCでは基本的にはオブジェクトの外から
インスタンス変数へのアクセスを許可していない
インスタンス変数への
アクセス
しかし,クラスAのメソッド定義の中からクラスAの
self以外のインスタンスが持つインスタンス変数には
直接アクセスできる
アクセスできるかのチェックはコンパイル時に
行われる
インスタンス変数への
アクセス
インスタンス変数にアクセスするには,そのインスタン
スオブジェクトが静的に(クラス名を型として使って)
型宣言されていなければならない
インスタンス変数への
アクセス
インスタンスオブジェクトobjが持つ
インスタンス変数myvarにアクセスする
この記法はポインタで構造体のメンバにアクセスする
場合と同じ
obj -> myvar
インスタンス変数への
アクセス
次のプログラムは色の三原色を表現したクラスで
2つの色の中間の色を計算する機能が付いている
インスタンス変数への
アクセス
三原色クラスのインタフェース部
(RGB.h)
インスタンス変数への
アクセス
三原色クラスの実装部
(RGB.m)
インスタンス変数への
アクセス
三原色クラスの実装部
(RGB.m)
インスタンス変数への
アクセス
三原色クラスをテストするmainプログラム
(rgbmain.m)
インスタンス変数への
アクセス
メソッドblendColorの中で引数のオブジェクトに対して
「->」を使ってインスタンス変数を直接参照している
引数が同じクラスのインスタンスなので
この方法でアクセス可能
他のクラスのインスタンス(スーパークラスのイン
スタンスであっても)この方法では参照できない
クラスを使って型が宣言されていない場合でも
アクセスできない
インスタンス変数の可視性
インスタンスオブジェクトが持つインスタンス変数には
外部から直接アクセスしない方がよい
どうしてもアクセスしたい場合許可する方法がある
インスタンス変数の可視性
インスタンス変数の外部への見せ方(見せるかどうか,
範囲はどこまでか)を可視性(visibility)と呼ぶ
ObjCではインスタンス変数に3種類の可視性を指定
できる
インスタンス変数の可視性
@private
宣言したクラス内のみでアクセス可能
サブクラスからはアクセスできない
同じクラスのインスタンスなら,メソッド内で
->演算子を使ってアクセスできる
インスタンス変数の可視性
@protected
宣言したクラス,およびそのサブクラス内で
アクセス可能
同じクラスのインスタンスなら,メソッド内で
->演算子を使ってアクセスできる
通常何も指定していないインスタンス変数は
このモード
インスタンス変数の可視性
@public
どこからでも,構造体のメンバのようにして
参照できる
インスタンス変数の可視性
@private @protected @public
同じクラス内 ○ ○ ○
同じクラス内で->を使って ○ ○ ○
サブクラスから ○ ○
サブクラスから->を使って ○
どこからでも ○
インスタンス変数の可視性
可視性の指定はクラスのインタフェース部の
インスタンス変数の宣言部分で行う
インスタンス変数の宣言の先頭,あるいは宣言と宣言
の間(;の後)に3つのコンパイラ指示子のいずれかを
記述できる
インスタンス変数の可視性
コンパイラ指示子の可視性の指定は,
その位置からインスタンス変数の宣言が終わるまで
別のコンパイラ指示子が出現するまで
その間のインスタンス変数に対して有効になる
インスタンス変数の可視性
コンパイラ指示子の出現順序に制限は無い
また,何回現れても構わない
インスタンス変数の可視性
@interface TableOfColors : HashTable
{
id delegate; //protected
@public
BOOL empty; //public
@private
id cache; //private
int cache_entries; //private
@protected
int entries; //protected
}
...
@end
つづく(多分)

Objc02