|
|
Log In | Not a Member? |
Contact ADC |
| < 前ページ次ページ > |
オブジェクト指向プログラムは、通常、さまざまなオブジェクトで作られています。Cocoaフレームワークをベースにしたプログラムでは、NSMatrixオブジェクト、NSWindowオブジェクト、NSDictionaryオブジェクト、NSFontオブジェクト、NSTextオブジェクト、その他多くのオブジェクトを使用します。また、しばしば、同じ種類(クラス)の複数のオブジェクト(たとえば、NSArrayオブジェクトやNSWindowオブジェクト)を使用します。
Objective-Cでは、クラスを定義することでオブジェクトを定義します。クラス定義は、一種のオブジェクトのプロトタイプです。クラスのあらゆるメンバの一部になるインスタンス変数を宣言し、クラスの全オブジェクトが使用できるメソッドのセットを定義します。
コンパイラにより、クラスごとに1つだけアクセス可能なオブジェクトが作成されます。これがクラスオブジェクトで、そのクラスに属する新しいオブジェクトの構築方法を知っています(そのため、伝統的に「ファクトリオブジェクト」と呼ばれています)。クラスオブジェクトはコンパイル済みのクラスであり、それによって構築されるオブジェクトがクラスのインスタンスです。プログラムの主な作業を実行するオブジェクトは、実行時にクラスオブジェクトによって作成されたインスタンスです。
クラスの全インスタンスは同じメソッドのセットを持っており、すべてが同じ鋳型に基づくインスタンス変数のセットを持っています。オブジェクトはそれぞれ自身のインスタンス変数を獲得しますが、メソッドは共有されます。
規則により、クラス名は大文字で始まり(「Rectangle」など)、インスタンス名は通常小文字で始まります(「myRect」など)。
継承
クラスの型
クラスオブジェクト
ソースコードにおけるクラス名
クラス定義は追加的に定義していきます。つまり、定義する新しいクラスはすべて別のクラスをベースにしており、そのメソッドとインスタンス変数を継承します。新しいクラスでは、継承したものに追加や変更を加えるだけです。継承したコードを複製する必要はありません。
継承によって、すべてのクラスがリンクされ、1つのクラスをルートに持つ階層ツリーを形成します。Foundationフレームワークをベースにしたコードを書いている場合、そのルートクラスは一般的にNSObjectです。(ルートクラスを除く)すべてのクラスには、ルートに1ステップ近いスーパークラスがあります。また、(ルートクラスを含む)どのクラスも、ルートから1ステップ遠い任意のサブクラスのスーパークラスになります。図 1-1はドロープログラムで使用されるいくつかのクラスの階層を示したものです。
この図に示すように、SquareクラスはRectangleのサブクラス、RectangleクラスはShapeのサブクラス、ShapeはGraphicのサブクラス、GraphicはNSObjectのサブクラスです。継承は累積的なものです。したがって、Squareオブジェクトは、特にSquareのために定義されたメソッドとインスタンス変数だけでなく、Rectangle、Shape、Graphic、およびNSObjectのために定義されたメソッドとインスタンス変数も持っています。これは簡単にいえば、SquareオブジェクトはSquareであるだけでなく、Rectangle、Shape、Graphic、およびNSObjectでもあるということです。
そのため、NSObject以外のすべてのクラスは、別のクラスを特殊化または適合化したものと考えることができます。下位のサブクラスはそれぞれ継承したものの累計に変更を加えます。たとえば、Squareクラスでは、RectangleをSquareに変えるのに必要な最小限のものだけが定義されます。
クラスを定義する際には、そのスーパークラスを宣言することでクラスを階層にリンクします。作成するすべてのクラスは、(新しいルートクラスを定義しないかぎり)別のクラスのサブクラスである必要があります。スーパークラスとして利用できるクラスは多数あります。Cocoaには、NSObjectクラスと、250以上の追加クラスの定義を含む複数のフレームワークがあります。そのまま使用できる(そのままプログラムに組み込める)クラスもあります。また、サブクラスを定義して自分自身のニーズに合わせられるものもあります。
フレームワーククラスには、必要なものをほとんど定義しながら、一部の詳細はサブクラスの実装に任されているものもあります。したがって、コードを少しだけ記述して、フレームワークのプログラマによる成果を再利用すれば、非常に高度なオブジェクトを作成することができます。
NSObjectはルートクラスなので、スーパークラスはありません。Objective-Cオブジェクトとオブジェクトの対話の基本的フレームワークは、NSObjectで定義されています。NSObjectは、自身を継承するクラスとクラスのインスタンスに、オブジェクトとして動作し、ランタイムシステムと連携する能力を与えます。
特別な動作を別のクラスから継承する必要のないクラスでも、NSObjectクラスのサブクラスにするべきです。クラスのインスタンスには、少なくとも実行時にObjective-Cオブジェクトのように動作する能力が必要です。このような能力をNSObjectクラスから継承するほうが、新しいクラス定義で新たに作成することに比べると、より簡単で信頼性も高くなります。
注: 新しいルートクラスを実装するのは、慎重な作業が必要で、多くの危険が潜んでいます。そのクラスは、インスタンスを割り当て、クラスに接続し、ランタイムシステムで識別するなど、NSObjectクラスが実行する多くのことを複製しなければなりません。そのため、通常は、ルートクラスとしてCocoaに提供されているNSObjectクラスを使用してください。詳細については、FoundationフレームワークドキュメントでNSObjectクラスとNSObjectプロトコルを参照してください。
クラスオブジェクトが新しいインスタンスを作成すると、新しいオブジェクトにはそのクラスに定義されたインスタンス変数だけでなく、そのスーパークラスと、スーパークラスのスーパークラスに定義されたインスタンス変数も、ルートクラスまで遡って含まれます。したがって、NSObjectクラスで定義された isaインスタンス変数は、あらゆるオブジェクトの一部になります。isaは各オブジェクトをそのクラスに結び付けます。
図 1-2はRectangleの個々の実装に定義できるインスタンス変数と、それらがどこから継承されているかを示しています。オブジェクトをRectangleにする変数がオブジェクトをShapeにする変数に追加され、オブジェクトをShapeにする変数がオブジェクトをGraphicにする変数に追加されるというようになっている点に注目してください。
クラスでのインスタンス変数の宣言は必須ではありません。新しいメソッドを単に定義して、何らかのインスタンス変数が必要な場合は、継承するインスタンス変数に依存することができます。たとえば、Squareクラスは、自身の新しいインスタンス変数を宣言しなくてもかまいません。
オブジェクトは、そのクラスに定義されたメソッドだけでなく、そのスーパークラスと、スーパークラスのスーパークラスに定義されたメソッドにも、階層のルートクラスまで遡ってアクセスできます。たとえば、Squareオブジェクトは、自身のクラスで定義されたメソッドはもちろん、Rectangle、Shape、Graphic、およびNSObjectクラスで定義されたメソッドも使用することができます。
したがって、プログラムで定義する新しいクラスは、階層で上位にあるすべてのクラスのために書かれたコードを利用することができます。このような継承は、オブジェクト指向プログラミングの主要なメリットです。Cocoaの提供するオブジェクト指向フレームワークのいずれかを使用すると、プログラムはフレームワーククラスのコードとして実装されている基本機能を利用することができます。追加する必要があるのは、標準機能をアプリケーションに合わせてカスタマイズするコードだけです。
クラスオブジェクトも、階層で上位にあるクラスを継承します。ただし、クラスオブジェクトはインスタンス変数を持たないため(インスタンスだけが持ちます)、メソッドだけを継承します。
継承には1つの便利な例外があります。新しいクラスを定義する際に、階層の上位にあるクラスで定義されたメソッドと同じ名前で、新しいメソッドを実装することができます。新しいメソッドはオリジナルをオーバーライドします。新しいクラスのインスタンスはオリジナルではなく新しいメソッドを実行し、新しいクラスのサブクラスもオリジナルではなく新しいメソッドを継承します。
たとえば、Rectangleは独自のdisplayを定義することによって、Graphicに定義されたdisplayメソッドをオーバーライドします。Graphic版のメソッドはGraphicクラスを継承するあらゆるオブジェクトで利用可能ですが、Rectangleオブジェクトでは利用できません。Rectangleオブジェクトでは代わりに、独自版のdisplayが実行されます。
メソッドをオーバーライドするとオリジナルを継承できなくなりますが、新しいクラスで定義された他のメソッドは、再定義されたメソッドをスキップして、オリジナルを見つけることができます(詳細については、selfとsuperに対するメッセージを参照してください)。
また、再定義したメソッドには、オーバーライド対象メソッドを組み込むことができます。この場合、新しいメソッドはオーバーライド対象メソッドを完全に置き換えるのではなく、改良または変更するにすぎません。階層内の複数のクラスで同じメソッドを定義し、それぞれの新しいバージョンでオーバーライド対象メソッドを組み込んでいる場合、実質的には元のメソッドの実装がすべての対象クラスに拡散されていることになります。
サブクラスは継承したメソッドをオーバーライドできますが、継承したインスタンス変数はオーバーライドできません。オブジェクトは継承するすべてのインスタンス変数にメモリを割り当てるため、同じ名前で新しいインスタンス変数を宣言して、継承した変数をオーバーライドすることはできません。オーバーライドを試みると、コンパイラがエラーを表示します。
クラスの中には、他のクラスに継承されることのみを目的としているものや、主に他のクラスに継承されることを目的としているものもあります。このような抽象クラスは、さまざまなサブクラスが使用できるメソッドとインスタンス変数を共通の定義にグループ化します。抽象クラスは通常、単独では不完全ですが、サブクラスを実装する負担を軽減するのに役立つコードが含まれています。
NSObjectクラスは抽象クラスの典型的な例です。プログラムは多くの場合、NSObjectのサブクラスを定義して、そのサブクラスに属するインスタンスを使用しますが、NSObjectクラスに直接属するインスタンスを使用することはありません。NSObjectインスタンスは何かに使用できるものではなく、特に何かを行う機能のない汎用オブジェクトです。
NSViewクラスは、直接使用される可能性のある抽象クラスの一例です。
抽象クラスには多くの場合、アプリケーションの構造を定義するのに役立つコードが含まれています。抽象クラスのサブクラスを作成すると、新しいクラスのインスタンスは特に問題なくアプリケーション構造に適合し、他のオブジェクトと自動的に連携します
(抽象クラスを使用するにはサブクラスが必要であるため、抽象スーパークラスと呼ばれることもあります)。
クラス定義は特定の種類のオブジェクトの仕様です。したがって、クラスは、実質的にデータ型を規定します。このデータ型は、クラスで定義されるデータ構造(インスタンス変数)だけでなく、定義に含まれている動作(メソッド)もベースにしています。
sizeof演算子の引数などのように、C言語において型指定子を指定できる場所ならば、クラス名をソースコードに記述できます。
int i = sizeof(Rectangle); |
idの代わりにクラス名を使用して、オブジェクトの型を指定することができます。
Rectangle *myRect; |
このような方法でのオブジェクト型の宣言は、オブジェクトの種類に関する情報をコンパイラに提供するため、静的な型定義と呼ばれています。idがオブジェクトへのポインタとして定義されているのと同じように、オブジェクトはクラスへのポインタとして静的に型定義されています。オブジェクトは必ずポインタとして型定義されます。静的な型定義はポインタを明示的なものにし、idはポインタを隠蔽します。
静的な型定義により、コンパイラは型チェックを行うことができ(たとえば、オブジェクトがメッセージを受信しても応答できないと警告する)、一般的にidとして型定義されたオブジェクトに適用される制限を緩和することができます。さらに、他の人に対して、ソースコードの意図を明らかにします。ただし、静的な型定義は動的バインディングを無効化するものではなく、実行時におけるレシーバのクラスの動的な決定を変更するものでもありません。
オブジェクトは、自身のクラスまたは継承するクラスとして静的に型定義することができます。たとえば、継承によりRectangleはGraphicの一種であるため、RectangleインスタンスはGraphicクラスに合わせて静的に型定義できます。
Graphic *myRect; |
これが可能なのは、RectangleがGraphicであるためです。ShapeとRectangleのインスタンス変数とメソッドの機能も持っているため、RectangleはGraphicを越える機能を持ちますが、それでもやはりGraphicクラスであることに変わりありません。型チェックのために、コンパイラはmyRectがGraphicであると見なしますが、実行時にはRectangleとして扱われます。
静的な型定義とそのメリットの詳細については、静的な動作の実現を参照してください。
インスタンスは、実行時にその型を明らかにすることができます。isMemberOfClass:メソッドはNSObjectクラスで定義されており、レシーバが特定のクラスのインスタンスかどうかをチェックします。
if ( [anObject isMemberOfClass:someClass] ) |
... |
isKindOfClass:メソッドもNSObjectクラスで定義されており、レシーバが特定のクラスを継承しているどうか、すなわちそのクラスのメンバかどうか(指定のクラスがレシーバに継承パスにあるかどうか)をチェックします。
if ( [anObject isKindOfClass:someClass] ) |
... |
isKindOfClass:がYESを返すクラスのセットは、レシーバを静的に型定義できるものと同じセットです。
イントロスペクションで調べられる情報は型情報に限られていません。この章の後のセクションでは、クラスオブジェクトを返したり、オブジェクトがメッセージに応答できるかどうかを報告したり、その他の情報を明らかにするメソッドについて説明します。
isKindOfClass:、isMemberOfClass:および関連するメソッドの詳細については、FundationフレームワークリファレンスのNSObjectクラス仕様を参照してください。
クラス定義には、次のようなさまざまな情報が含まれており、その大部分はクラスのインスタンスに関するものです。
クラスとそのスーパークラスの名前
インスタンス変数のセットを記述したテンプレート
メソッド名およびその戻り値と引数の型の宣言
メソッドの実装
これらの情報は、ランタイムシステムが利用できるデータ構造にまとめられ記録されます。コンパイラは、クラスを表すオブジェクト、すなわちクラスオブジェクトを1つだけ作成します。クラスオブジェクトは、クラスに関するすべての情報にアクセスできます。これは、主にクラスのインスタンスを表す情報です。クラス定義によって規定されるプランに従って、新しいインスタンスを生成することができます。
クラスオブジェクトはクラスインスタンスのプロトタイプを保持していますが、それ自体はインスタンスではありません。クラスオブジェクトは独自のインスタンス変数を持っていませんし、クラスのインスタンスを対象としたメソッドを実行することができません。しかし、クラス定義は特にクラスオブジェクトを対象としたメソッド、すなわちインスタンスメソッドではなく、クラスメソッドを含むことができます。インスタンスがインスタンスメソッドを継承するのと同じように、クラスオブジェクトは階層の上位にあるクラスからクラスメソッドを継承します。
ソースコードでは、クラスオブジェクトはクラス名で表されます。次の例では、RectangleクラスがNSObjectクラスから継承したメソッドを使用してクラスのバージョン番号を返します。
int versionNumber = [Rectangle version]; |
ただし、クラス名は、メッセージ式の単なるレシーバとしてのクラスオブジェクトを表します。その他の場合は、インスタンスまたはクラスに対してidクラスを返すように要求する必要があります。どちらもclassメッセージに応答します。
id aClass = [anObject class]; |
id rectClass = [Rectangle class]; |
上記の例に示すように、クラスオブジェクトは他のオブジェクトと同様に、idとして型定義できます。しかし、クラスオブジェクトは、Classデータ型としてより明示的に型定義することもできます。
Class aClass = [anObject class]; |
Class rectClass = [Rectangle class]; |
すべてのクラスオブジェクトはClass型です。この型名をクラスに使用するのは、クラス名を使用してインスタンスを静的に型定義するのと同じです。
つまり、クラスオブジェクトは、動的に型定義したり、メッセージを受信したり、他のクラスからメソッドを継承することができる完全なオブジェクトです。クラスオブジェクトが特別であるのは、コンパイラによって作成され、クラス定義に基づいて構築されるものを除けば自身のデータ構造(インスタンス変数)がなく、実行時にインスタンスを生成するエージェントであるという点だけです。
注: コンパイラは、各クラスの「メタクラスオブジェクト」も構築します。クラスオブジェクトがクラスのインスタンスを示すのと同じように、メタクラスオブジェクトはクラスオブジェクトを示します。ただし、インスタンスやクラスオブジェクトにメッセージを送信できるのに対し、メタクラスオブジェクトはランタイムシステムによって内部的に使用されるだけです。
クラスオブジェクトの主要な機能は、新しいインスタンスを作成することです。次のコードは、Rectangleクラスに対して新しいRectangleインスタンスを作成して、myRect変数に割り当てるように指示します。
id myRect; |
myRect = [Rectangle alloc]; |
allocメソッドは、新しいオブジェクトのインスタンス変数に動的にメモリを割り当て、すべてを0に初期化します。ただし、新しいインスタンスをそのクラスに結び付けるisa変数は除きます。オブジェクトが有用であるためには、通常は完全に初期化する必要があります。その初期化を行うのがinitメソッドの機能です。初期化は、通常、割り当ての直後に行います。
myRect = [[Rectangle alloc] init]; |
この章のこれまでの例で示したメッセージをmyRectで受信するには、先に上記のようなコードが必要です。allocメソッドは新しいインスタンスを返し、そのインスタンスがinitメソッドを実行して初期状態に設定します。すべてのクラスオブジェクトに、新しいオブジェクトを生成するメソッド(allocなど)が少なくとも1つはあり、すべてのインスタンスは、それを使えるように準備するメソッド(initなど)が少なくとも1つあります。初期化メソッドは多くの場合、特定の値を渡せる引数を持っており、引数にラベルを付けるキーワードを持っていますが(たとえば、新しいRectangleインスタンスを初期化するinitWithPosition:size:のようなメソッドなど)、それらはすべて「init」という文字列で始まります。
クラスをオブジェクトとして扱うのは、Objective-C言語では偶然ではありません。それは、意図的で、ときには意外な設計上のメリットを持つ選択なのです。たとえば、クラスが制限のないセットに属している場合は、クラスでオブジェクトをカスタマイズすることができます。Application Kitでは、たとえば、特定の種類のNSCellオブジェクトを使用してNSMatrixオブジェクトをカスタマイズできます。
NSMatrixオブジェクトは、セルを表す個々のオブジェクトを作成することができます。オブジェクトの作成は、行列を最初に初期化する際と、あとで新しいセルが必要なときに可能です。NSMatrixオブジェクトが画面上に描画する目に見える行列は、実行時にユーザの操作に応じて拡大/縮小することができます。拡大する場合、行列は、追加される新しいスロットを満たすため、新しいオブジェクトを生成する必要があります。
それらを、どのようなオブジェクトにすべきかについて次に説明します。それぞれの行列は1種類のNSCellのみを表示しますが、種類はさまざまにあります。図 1-3の継承階層は、Application Kitによって提供されるものの一部です。すべてが汎用NSCellクラスを継承します。
行列がNSCellオブジェクトを作成するとき、それらは一連のボタンやスイッチを表示するNSButtonCellオブジェクトであるべきでしょうか。それともユーザがテキストの入力や編集ができるフィールドを表示するNSTextFieldCellオブジェクトであるべきでしょうか。あるいは他の種類のNSCellにするべきでしょうか。NSMatrixオブジェクトは、あらゆる種類のセルに対応しなければなりません。まだ作成されていない型についても考慮しなければなりません。
この問題に対する1つの解決策は、NSMatrixクラスを抽象クラスとして定義し、それを使用する全員にサブクラスの宣言と、新しいセルを生成するメソッドの実装を義務付けることです。NSMatrixクラスのユーザは必要なメソッドを実装するため、作成したオブジェクトが適切な型であることを保証できます。
しかし、これは、NSMatrixクラスでなされているべき作業を他人に求めることになるため、クラスの数が不必要に増えます。アプリケーションは複数のNSMatrixを必要とし、それぞれが異なる種類のNSCellを持つ可能性があるため、NSMatrixのサブクラスで雑然とする可能性があります。新しいNSCellを作成するたびに、新しいNSMatrixも定義する必要があります。さらに、別々のプロジェクトのプログラマが、同じ処理を実行するほとんど同じようなコードを書くことになります。すべてはNSMatrixがしないことを補うためです。
より優れた解決策、すなわちNSMatrixクラスが実際に採用している解決策は、NSMatrixインスタンスを一種のNSCellで(クラスオブジェクトで)初期化することです。NSMatrixは、NSMatrixが空のスロットを満たすために使用するNSCellオブジェクトを表すクラスオブジェクトを渡すsetCellClass:メソッドを定義します。
[myMatrix setCellClass:[NSButtonCell class]]; |
NSMatrixオブジェクトは、最初に初期化するとき、および、より多くのセルを含むようにサイズ変更するときに、クラスオブジェクトを使用して新しいセルを生成します。クラスが、メッセージで渡したり、変数に割り当てることのできるオブジェクトでなければ、このようなカスタマイズは困難です。
新しいクラスを定義する際、インスタンス変数を指定することができます。クラスのあらゆるインスタンスが、宣言された変数のコピーをそれぞれに保持することができ、各オブジェクトが自身のデータを制御します。ただし、インスタンス変数に対応する「クラス変数」はありません。クラスに提供されるのは、クラス定義に基づいて初期化された内部データ構造だけです。また、クラスオブジェクトは、どのインスタンスのインスタンス変数にもアクセスできません。すなわち、インスタンス変数の初期化、読み取り、または変更ができません。
クラスのすべてのインスタンスがデータを共有するには、何らかの外部変数が必要になります。最も簡単に実現するには、次のコードで示すようにクラス実装ファイルで変数を宣言します。
int MCLSGlobalVariable; |
@implementation MyClass |
// 実装が続く |
より洗練された実装では、static変数を宣言し、それらを管理するクラスメソッドを提供します。static変数を宣言すると、その有効範囲は当該クラスのみ、厳密にはそのファイルに実装されたクラスの部分に限定されます(したがって、インスタンス変数と異なり、静的変数をサブクラスによって継承したり、サブクラスで直接操作したりできません)。このパターンは、シングルトンのようなクラスの共有インスタンスの宣言によく使用されます(シングルトンについては、「シングルトンインスタンスの作成」を参照してください)。
static MyClass *MCLSSharedInstance; |
@implementation MyClass |
+ (MyClass *)sharedInstance |
{ |
// 共有インスタンスの有無を確認する |
// 必要なら作成する |
return MCLSSharedInstance; |
} |
// 実装が続く |
静的な変数は、クラスオブジェクトに対して、インスタンスを生成する「ファクトリ」以上の機能を与える役割も持ち、それ自体で完全で多目的なオブジェクトになることができます。クラスオブジェクトは、作成するインスタンスの間を取り持ったり、作成済みオブジェクトのリストからインスタンスを削除したり、アプリケーションに不可欠な他の処理を管理するために使用することができます。特定クラスのオブジェクトが1つだけ必要な場合は、オブジェクトの状態をすべて静的な変数に入れて、クラスメソッドのみを使用するようにできます。これにより、インスタンスの割り当てと初期化のステップを省けます。
クラスオブジェクトをインスタンスの割り当て以外に使用する場合は、インスタンスのように初期化する必要があります。プログラムはクラスオブジェクトを割り当てませんが、Objective-Cはプログラムがそれらを初期化する手段を提供します。
クラスが静的な変数またはグローバル変数を利用する場合は、initializeメソッドがそれらの初期値の設定に適しています。たとえば、クラスがインスタンスの配列を保持する場合、initializeメソッドでその配列を準備し、さらに1つか2つのデフォルトインスタンスを割り当てて用意しておくことができます。
ランタイムシステムは、クラスが他のメッセージを受信する前、およびそのスーパークラスがinitializeメッセージを受信した後に、initializeメッセージをすべてのクラスオブジェクトに送信します。これにより、クラスは自身が使用される前に、ランタイム環境を準備する機会を与えられます。初期化が不要な場合は、メッセージに応えるinitializeメソッドを書く必要はありません。
継承があるため、スーパークラスがすでにinitializeメッセージを受信していても、initializeメソッドを実装していないクラスへ送信されたinitializeメッセージは、スーパークラスへ転送されます。たとえば、クラスAはinitializeメソッドを実装しており、クラスBはクラスAを継承しているけれどもinitializeメソッドは実装していないと仮定します。クラスBが最初のメッセージを受信する直前に、ランタイムシステムはクラスBへinitializeを送信します。ただし、クラスBはinitializeを実装していないため、クラスAのinitializeが代わりに実行されます。そのため、クラスAでは、初期化ロジックが1回だけ実行されるようにしなければなりません。
初期化ロジックが複数回実行されるのを防ぐには、initializeメソッドを実装する際にリスト 1-1に示すテンプレートを使用します。
リスト 1-1 initializeメソッドの実装
+ (void)initialize |
{ |
static BOOL initialized = NO; |
if (!initialized) { |
// ここで初期化を実行する |
... |
initialized = YES; |
} |
} |
注: ランタイムシステムはクラスごとにinitializeを送信します。そのため、クラス内のinitializeメソッド実装で、スーパークラスへinitializeメッセージを送信する必要はありません。
クラスオブジェクト、インスタンスオブジェクトを問わず、オブジェクトはすべて、ランタイムシステムに対するインターフェイスが必要です。クラスオブジェクトとインスタンスはどちらも、その能力についてのイントロスペクションを可能にし、継承階層における位置を報告できる必要があります。このインターフェイスを提供するのはNSObjectクラスの役割です。
NSObjectのメソッドを二度(一度はインスタンスにランタイムインターフェイスを提供するため、もう一度はそのインターフェイスをクラスオブジェクトにコピーするため)実装する必要がないように、クラスオブジェクトには、ルートクラスで定義されているインスタンスメソッドを実行する特別許可が与えられます。クラスオブジェクトがクラスメソッドで応えられないメッセージを受信すると、ランタイムシステムは、メッセージに応えられるルートインスタンスメソッドがあるかどうかを調べます。クラスオブジェクトが実行できるインスタンスメソッドは、ルートクラスに定義されているものだけであり、指定の作業を実行できるクラスメソッドがない場合にのみ実行できます。
ルートインスタンスメソッドを実行する、クラスオブジェクトの特別な能力の詳細については、FoundationフレームワークリファレンスのNSObjectクラス仕様を参照してください。
ソースコードでは、まったく異なる2つのコンテキストでのみクラス名を使用することができます。これらのコンテキストは、データ型およびオブジェクトとしてのクラスの、ニ重の役割を反映しています。
クラス名は、オブジェクトの種類を示す型名として使用することができます。たとえば、
Rectangle * anObject; |
この場合、anObjectは、Rectangleのポインタとなるように静的に型定義されています。コンパイラは、対象がRectangleインスタンスのデータ構造と、Rectangleクラスで定義されそこから継承したインスタンスメソッドを持っているものと想定します。静的な型定義により、コンパイラの型チェックを強化し、ソースコードの自己文書化をさらに進めることができます。詳細については、静的な動作の実現を参照してください。
静的に型定義できるのはインスタンスだけです。クラスオブジェクトは、クラスのメンバではなくClassデータ型に属するため、静的に型定義できません。
メッセージ式のレシーバとしてのクラス名は、クラスオブジェクトを表します。このような使用法は、これまでのいくつかの例で示しました。クラス名は、メッセージのレシーバとしてのみクラスオブジェクトを表すことができます。他のコンテキストでは、クラスオブジェクトに(クラスメッセージを送信して)idを明らかにするように要求する必要があります。次の例では、RectangleクラスをisKindOfClass:メッセージの引数として渡しています。
if ( [anObject isKindOfClass:[Rectangle class]] ) |
... |
引数として単純に「Rectangle」という名前を使用するのは正しくありません。このクラス名はレシーバとしてのみ指定できます。
コンパイル時にクラス名が分からなくても、実行時に文字列として持っていれば、NSClassFromStringを使用してクラスオブジェクトを返すことができます。
NSString *className; |
... |
if ( [anObject isKindOfClass:NSClassFromString(className)] ) |
... |
渡された文字列が有効なクラス名でない場合、この関数はnilを返します。
クラス名は、グローバル変数や関数名と同じネームスペースに存在します。クラスとグローバル変数は、同じ名前を持つことができません。クラス名は、Objective-Cでグローバルに認識できるほぼ唯一の名前です。
| < 前ページ次ページ > |
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 |