|
|
Log In | Not a Member? |
Contact ADC |
| < 前ページ次ページ > |
オブジェクト宣言においてidの代わりに、クラス名のポインタを使用する場合は、次のようになります。
Rectangle *thisObject; |
コンパイラは、宣言した変数の値を、宣言で指定されたクラスのインスタンス、または指定されたクラスを継承するクラスのインスタンスのいずれかに限定します。上記の例では、thisObjectは何らかのRectangleに限定されます。
静的に型定義したオブジェクトは、idとして宣言されたオブジェクトと同じ内部データ構造を持ちます。型はオブジェクトには影響しません。コンパイラに提供するオブジェクトに関する情報の量と、ソースコードを読む他の人が得られる情報の量にのみ影響します。
静的な型定義は、実行時のオブジェクトの処理方法にも影響しません。静的に型定義したオブジェクトは、id型のインスタンスを作成するのと同じクラスメソッドによって動的に割り当てられます。SquareがRectangleのサブクラスの場合、次のコードはRectangleのインスタンス変数だけでなく、Squareのインスタンス変数をすべて持ったオブジェクトを生成します。
Rectangle *thisObject = [[Square alloc] init]; |
静的に型定義したオブジェクトに送信するメッセージは、idとして型定義したオブジェクトと同じように、動的にバインドされます。さらに、静的に型定義したレシーバの実際の型は、実行時にメッセージング処理の一環として決定されます。次の例は、thisObjectに送信されるdisplayメッセージです。
[thisObject display]; |
これはRectangleスーパークラスのメソッドではなく、Squareクラスに定義されているメソッドのバージョンを実行します。
オブジェクトに関する詳細な情報をコンパイラに提供することで、静的な型定義にはidとして型定義したオブジェクトにはない可能性が広がります。
ある状況において、静的な型定義はコンパイル時の型チェックを可能にします。
静的な型定義により、同じ名前のメソッドは同一の戻り型と引数型を持つ必要がある、という制限からオブジェクトが解放されます。
また、構造体ポインタ演算子を使用して、オブジェクトのインスタンス変数に直接アクセスすることもできます。
最初の2つのトピックについては、以降のセクションで説明します。3番目のトピックはクラスの定義で説明します。
静的な型定義によって追加情報が提供されるため、コンパイラは次の2つの状況下において、より適切な型チェックサービスを行うことができます。
静的に定義したレシーバにメッセージを送信すると、コンパイラはレシーバが応答できることを確認できます。レシーバがメッセージに指定されているメソッドにアクセスできない場合は、警告が発せられます。
静的に型定義したオブジェクトを静的に型定義した変数に割り当てると、コンパイラは型の互換性があるかどうかを確認します。互換性がない場合は、警告が発せられます。
代入するオブジェクトのクラスが、代入先の変数のクラスと同じであるか、そのクラスを継承する場合には、代入を行っても警告は出ません。次の例は、これを示しています。
Shape *aShape; |
Rectangle *aRect; |
aRect = [[Rectangle alloc] init]; |
aShape = aRect; |
ここで、RectangleはShapeの一種である(RectangleクラスはShapeを継承する)ため、aRectをaShapeに代入することができます。ただし、2つの変数の役割を逆にしてaShapeをaRectに代入した場合、コンパイラが警告を発します。すべてのShapeがRectangleであるとはかぎらないからです(図 1-2も参照してください。この図は、ShapeとRectangleを含むクラス階層を示しています)。
代入演算子のどちらかの側にある式がidである場合は、チェックが行われません。静的に型定義したオブジェクトをidに自由に代入したり、idを静的に型定義したオブジェクトに自由に代入することができます。allocやinitのようなメソッドはidを返すため、コンパイラは静的に型定義した変数に対して互換性のあるオブジェクトが返されることを保証しません。次のコードはエラーが起きる可能性はありますが、指定は可能です。
Rectangle *aRect; |
aRect = [[Shape alloc] init]; |
一般に、異なるクラスで同じセレクタ(同じ名前)を持つメソッドは、戻り型と引数型も同じでなければなりません。この制約は動的バインディングを可能にするためにコンパイラによって課せられます。メッセージレシーバのクラス(そして実行を要求されるメソッドに関するクラス固有の詳細)はコンパイル時には分からないため、コンパイラは同じ名前のメソッドをすべて同様に処理する必要があります。コンパイラがランタイムシステムのためにメソッドの戻り型と引数型に関する情報を準備する際に、メソッドセレクタごとにメソッド記述を1つだけ作成します。
しかし、メッセージを静的に型定義したオブジェクトに送信する場合、コンパイラはレシーバのクラスを認識しています。コンパイラはメソッドに関するクラス固有の情報にアクセスできます。したがって、メッセージは戻り型と引数型に関する制限から解放されます。
インスタンスは、自身のクラスまたは継承元のクラスに静的に型定義することができます。たとえば、すべてのインスタンスをNSObjectとして静的に型定義できます。
ただし、コンパイラは型指定のクラス名にのみ基づいて、静的に型定義されたオブジェクトのクラスを把握し、それに従って型チェックを実行します。したがって、インスタンスを継承クラスとして型定義すると、コンパイラが実行時に起こると予測したことと、実際に起こることとの間に矛盾が生じる可能性があります。
たとえば、RectangleインスタンスをShapeとして静的に型定義する場合、次のようになります。
Shape *myRect = [[Rectangle alloc] init]; |
この場合、コンパイラはRectangleをShapeとして処理します。ここでRectangleメソッドを実行するメッセージをオブジェクトに送信します。
BOOL solid = [myRect isFilled]; |
この場合、コンパイラがエラーを報告します。isFilledメソッドはShapeクラスではなく、Rectangleクラスで定義されているからです。
しかし、今度はShapeクラスが認識するメソッドを実行するメッセージを送信したとします。
[myRect display]; |
Rectangleがメソッドをオーバーライドしていても、コンパイラはエラーを報告しません。しかし実行時には、Rectangleバージョンのメソッドが実行されます。
同様に、Upperクラスで、doubleを返すworryメソッドを宣言したとします。
- (double)worry; |
また、UpperのMiddleサブクラスでメソッドをオーバーライドし、新しい戻り型を宣言します。
- (int)worry; |
インスタンスをUpperクラスとして静的に型定義した場合、コンパイラはworryメソッドがdoubleを返すと予測し、インスタンスをMiddleクラスとして型定義した場合、worryがintを返すと予測します。MiddleインスタンスをUpperクラスとして型定義すると、明らかにエラーが生じます。コンパイラは、オブジェクトに送信されるworryメッセージがdoubleを返すことをランタイムシステムに通知しますが、実行時には実際に返されるのがintなのでエラーが発生します。
静的な型定義は、名前が同じメソッドを、同じ戻り型と引数型を持たなければならないという制限から解放しますが、確実に実行できるのはメソッドがクラス階層の異なる分岐で宣言されている場合だけです。
| < 前ページ次ページ > |
Last updated: 2007-10-31
|
Get information on Apple products.
Visit the Apple Store online or at retail locations. 1-800-MY-APPLE Copyright © 2007 Apple Inc. All rights reserved. | Terms of use | Privacy Notice |