Apple Developer Connection
Advanced Search
Member Login Log In | Not a Member? Contact ADC

< 前ページ次ページ >

セレクタ

効率化のため、コンパイル済みのコードでは完全なASCII名をメソッドセレクタとして使用しません。その代わりに、コンパイラは各メソッド名をテーブルに書き込み、その名前を実行時にメソッドを示す一意の識別子と組み合わせます。ランタイムシステムによって、各識別子が一意であることが保証されます。2つのセレクタが同じであることはなく、同じ名前のメソッドはすべて同じセレクタに対応します。コンパイル済みのセレクタは、他のデータと区別するため、特別な型SELに割り当てられます。有効なセレクタは0であることはありません。メソッドへのSEL識別子の割り当てはシステムに任せる必要があります。任意に割り当てても無駄になります。

@selector()ディレクティブにより、Objective-Cのソースコードは完全なメソッド名ではなく、コンパイル済みのセレクタを参照することができます。次の例では、setWidth:height:のセレクタを、setWidthHeight変数に代入しています。

SEL  setWidthHeight;
setWidthHeight = @selector(setWidth:height:);

コンパイル時に@selector()ディレクティブを使用して、SEL変数に値を代入するのが最も効率的です。しかし、場合によっては、プログラムで実行時に文字列をセレクタに変換する必要があります。これを実行するには、NSSelectorFromString関数を使います。

setWidthHeight = NSSelectorFromString(aBuffer);

逆方向の変換も可能です。NSStringFromSelector関数はセレクタに対応するメソッド名を返します。

NSString *method;
method = NSStringFromSelector(setWidthHeight);

このようなランタイム関数については、Cocoaフレームワークリファレンスドキュメントで説明しています。

このセクションの内容:

メソッドとセレクタ
メソッドの戻り値と引数の型
実行時のメッセージ変更
ターゲット/アクションデザインパターン
メッセージングエラーの回避
動的メソッド解決


メソッドとセレクタ

コンパイル済みのセレクタは、メソッド実装ではなく、メソッド名を識別します。たとえば、Rectangleのdisplayメソッドには、他のクラスで定義されたdisplayメソッドと同じセレクタがあります。これはポリモーフィズム(多態性)と動的バインディングには不可欠です。これにより、さまざまなクラスに属するレシーバに、同じメッセージを送信することができます。メソッドの実装ごとに1つのセレクタがあったとしたら、メッセージは関数呼び出しと変わりません。

クラスメソッド、および同じ名前のインスタンスメソッドには、同じセレクタが割り当てられます。しかし、それらは別々のドメインに属するため、2つの間に混乱はありません。クラスでは、displayインスタンスメソッドに加えて、displayクラスメソッドを定義することができます。

メソッドの戻り値と引数の型

メッセージングルーチンはセレクタを通してのみメソッド実装にアクセスできるため、同じセレクタに対応するすべてのメソッドを同等に扱います。ルーチンは、セレクタに基づいてメソッドの戻り型と引数のデータ型を知ります。したがって、静的に型定義されたレシーバに送信されたメッセージを除いて、動的バインディングでは、同じ名前を持つメソッドのすべての実装で、戻り型と引数型が同じである必要があります(コンパイラはクラス型からメソッド実装を知ることができるため、静的に型定義されたレシーバはこの規則の例外です)。

同じ名前のクラスメソッドとインスタンスメソッドは、同じセレクタで表されますが、引数と戻り型が異なる可能性があります。

実行時のメッセージ変更

performSelector:performSelector:withObject:、およびperformSelector:withObject:withObject:メソッドはNSObjectプロトコルで定義されており、初期引数としてSEL識別子をとります。3つのメソッドはすべて、メッセージング関数に直接マップされます。たとえば、次のように記述します。

[friend performSelector:@selector(gossipAbout:)
    withObject:aNeighbor];

次のように記述しても同じです。

[friend gossipAbout:aNeighbor];

これらのメソッドにより、メッセージを受信するオブジェクトを変更できるのと同じように、実行時にメッセージを変更できます。メッセージ式を構成する両方の要素に変数名を使用することができます。

id   helper = getTheReceiver();
SEL  request = getTheSelector();
[helper performSelector:request];

上記の例では、レシーバ(helper)は実行時に(架空のgetTheReceiver関数によって)選択され、レシーバが実行するように要求されるメソッド(request)も実行時に(同様に架空のgetTheSelector関数によって)決められます。

注: performSelector:とその関連メソッドはidを返します。実行されるメソッドが別の型を返す場合は、適切な型にキャストする必要があります(ただし、キャストがすべての型に有効なわけではありません。メソッドはポインタまたはポインタと互換性のある型を返す必要があります)。

ターゲット/アクションデザインパターン

ユーザインターフェイス制御の処理において、Application Kitはレシーバとメッセージの両方を変更する方法を有効に利用しています。

NSControlオブジェクトは、アプリケーションへの指示を指定するのに使用できるグラフィカルデバイスです。大部分はボタン、スイッチ、ノブ、テキストフィールド、ダイヤル、メニュー項目など、現実世界の制御デバイスに似ています。ソフトウェアでは、これらのデバイスがアプリケーションとユーザの間に介在します。これらのデバイスは、キーボードやマウスなどのハードウェアデバイスから発されるイベントを解釈し、アプリケーション固有の命令に変換します。たとえば、「検索」というラベルの付いたボタンはマウスクリックを、アプリケーションが何かの検索を開始する命令に変換します。

Application Kitは制御デバイスを作成するためのテンプレートを定義し、「そのまま」使用できる独自のデバイスもいくつか定義します。たとえば、NSButtonCellクラスでは、NSMatrixに割り当てて、サイズ、ラベル、ピクチャ、フォント、およびキーボードショートカットを初期化できるオブジェクトを定義します。ユーザがボタンをクリックすると(あるいは、キーボードショートカットを使用すると)、NSButtonCellオブジェクトはアプリケーションに何かを実行するよう指示するメッセージを送信します。これを行うには、NSButtonCellオブジェクトを画像、サイズ、およびラベルだけでなく、どんなメッセージを送信し、誰に送信するかに関する指示においても初期化する必要があります。したがって、NSButtonCellインスタンスはアクションメッセージ、送信するメッセージで使用するメソッドセレクタ、ターゲット(メッセージを受信するオブジェクト)で初期化することができます。

[myButtonCell setAction:@selector(reapTheWind:)];
[myButtonCell setTarget:anObject];

ボタンセルは、NSObjectperformSelector:withObject:メソッドを使用してメッセージを送信します。すべてのアクションメッセージは1つの引数、つまりメッセージを送信する制御デバイスのidを受け取ります。

Objective-Cでメッセージを変更できないとしたら、すべてのNSButtonCellオブジェクトが同じメッセージを送信しなければならず、メソッドの名前がNSButtonCellソースコードで固定されます。ユーザ操作をアクションメッセージに変換するメカニズムを単に実装するのではなく、ボタンセルなどのコントロールでメッセージの内容に制約を課す必要が生じます。これでは、どのようなオブジェクトも、複数のボタンセルに対応するのが困難になります。ボタンごとに1つのターゲットを用意するか、ターゲットオブジェクトがメッセージを送信したボタンを検出し、それに応じて動作する必要が生じます。ユーザインターフェイスを再配置するたびに、アクションメッセージに応答するメソッドも実装する必要が生じます。幸いにも、Objective-Cでは、このように不要な複雑さが回避されています。

メッセージングエラーの回避

オブジェクトが、レパートリーにないメソッドを実行するメッセージを受信すると、エラーが発生します。これは、存在しない関数を呼び出した場合と同じ種類のエラーです。しかし、メッセージングは実行時に行われるため、多くの場合、プログラムを実行するまでエラーが明らかになりません。

メッセージセレクタが不変で、受信側オブジェクトのクラスが分かっている場合、このようなエラーを回避するのは比較的簡単です。プログラムを記述するときに、レシーバが応答できることを確認しておけばよいからです。レシーバが静的に型定義されていれば、コンパイラがそのテストを実行してくれます。

しかし、メッセージセレクタまたはレシーバのクラスが可変の場合、そのテストを実行時まで延期しなければならない場合もあります。NSObjectクラスで定義されているrespondsToSelector:メソッドを使用してレシーバがメッセージに応答できるかどうかを調べることができます。引数としてメソッドセレクタを受け取り、レシーバがセレクタに一致するメソッドにアクセスできるかどうかを返します。

if ( [anObject respondsToSelector:@selector(setOrigin::)])
    [anObject setOrigin:0.0 :0.0];
else
    fprintf(stderr, "%s can’t be placed\n",
        [NSStringFromClass([anObject class]) UTF8String]);

respondsToSelector:テストが特に重要なのは、コンパイル時に制御の及ぶ範囲内にないオブジェクトにメッセージを送信する場合です。たとえば、他から設定可能な変数によって指定されるオブジェクトにメッセージを送信するコードを記述する場合は、必ずメッセージに応答できるメソッドをレシーバが実装するようにします。

注: また、オブジェクトは、自身で直接応答できない場合、受信したメッセージを他のオブジェクトに転送するように準備することもできます。その場合、メッセージを別のオブジェクトに割り当てて間接的に応答しているにも関わらず、オブジェクトがメッセージを処理できるように見えます。詳細については、転送を参照してください。

動的メソッド解決

動的メソッド解決を使用して、クラスメソッドおよびインスタンスメソッドの実装を実行時に指定できます。詳細についてはランタイムシステム動的メソッド解決を参照してください。



< 前ページ次ページ >


Last updated: 2007-10-31




Did this document help you?
Yes: Tell us what works for you.

It’s good, but: Report typos, inaccuracies, and so forth.

It wasn’t helpful: Tell us what would have helped.
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