|
|
Log In | Not a Member? |
Contact ADC |
| < 前ページ次ページ > |
他の多くのプログラミング言語と同様に、Objective-Cは当初、単一のアドレス空間でシングルプロセスとして実行されるプログラム用に設計されました。
しかし、実行時に解決されるメッセージを介して比較的自己完結型の単位の間で通信が行われるオブジェクト指向モデルは、プロセス間通信にも非常に適していると考えられます。異なるアドレス空間(つまり、異なるタスク)や、同一タスクを実行する異なるスレッドに存在するオブジェクトどうしのObjective-Cメッセージのやり取りを想像するのは難しくありません。
たとえば、典型的なサーバ-クライアント間の対話では、クライアントタスクはサーバ内の指定されたオブジェクトに要求を送信し、サーバは特定のクライアントオブジェクトをターゲットにして通知や他の情報の送信を行います。
あるいは、ユーザコマンドを実行するために大量の計算を行う必要のある、対話型アプリケーションがあるとします。このアプリケーションは、処理の間ユーザに待つように通知するダイアログを単純に表示するか、ユーザ入力を受け付けるアプリケーションの主要部を解放しておくために処理作業を切り離して従属タスクに任せることができます。2つのタスク内のオブジェクトは、Objective-Cメッセージを介して通信を行います。
同様に、複数の異なるプロセスが、1つの文書を協力して編集することもできます。その場合、文書のデータの種類ごとに、異なる編集ツールが用意されているかもしれません。1つのタスクが画面上に統合的なユーザインターフェイスを表示する役割と、各種編集ツールを呼び出すユーザ操作を仕分ける役割を担うことができます。協力する他のタスクをそれぞれObjective-Cで記述し、ユーザインターフェイスとツールの間およびツールどうしの通信手段としてObjective-Cメッセージを使用できます。
Objective-Cのリモートメッセージングには、異なるアドレス空間にあるオブジェクトどうしの接続の確立、リモートアドレス空間内のオブジェクトを対象とするメッセージの検出、アドレス空間どうしでのデータ転送を行うことのできるランタイムシステムが必要です。また、2つのタスクの間のスケジュール調整を行う必要があります。つまり、リモートレシーバが処理から解放されて応答できるようになるまでメッセージを保持できる必要があります。
Cocoaには、ランタイムシステムにまさにこのような拡張を加える、分散オブジェクトアーキテクチャが組み込まれています。分散オブジェクトを使用すると、Objective-Cメッセージを別のタスクのオブジェクトに送信したり、メッセージを同じタスクの別のスレッドに実行させることができます(リモートメッセージを同一タスクの2つのスレッド間で送信する場合、スレッドは別々のタスクのスレッドどうしの場合とまったく同様に処理されます)。Cocoaの分散オブジェクトシステムは、ランタイムシステムの基礎の上に構築されており、Cocoaオブジェクトの基本的な動作を変更しないことに注意してください。
リモートメッセージを送信するには、アプリケーションはまずリモートレシーバとの接続を確立する必要があります。接続を確立すると、リモートオブジェクトに対するプロキシがアプリケーション自身のアドレス空間に用意されます。以降、アプリケーションはプロキシを通してリモートオブジェクトと通信します。プロキシは、リモートオブジェクトのアイデンティティを自分のアイデンティティとして使用し、独自のアイデンティティは持ちません。アプリケーションは、プロキシをリモートオブジェクトのように見なすことができ、多くの場合、プロキシはリモートオブジェクトも同然です。
リモートメッセージングについて、図 12-6 に示します。この場合、オブジェクトAはプロキシを通してオブジェクトBと通信し、BへのメッセージはBが応答できるようになるまでキュー内で待機します。
送信側と受信側は異なるタスクにあり、互いに関係なくスケジュールされます。そのため、送信側がメッセージを送信する準備ができていても、受信側の準備ができている保証はありません。したがって、到着メッセージはキュー内に置かれ、受信側アプリケーションの都合に合わせて取り出されます。
プロキシは、リモートオブジェクトに代わって動作するものではなく、そのクラスへのアクセスを必要とするものでもありません。また、プロキシは、オブジェクトのコピーではなく、軽量の代理です。ある意味において、プロキシは透過的です。受信したメッセージをリモートレシーバに受け渡して、プロセス間通信を管理するだけです。その主な機能は、ローカルアドレスを持っていないオブジェクトのためにローカルアドレスを提供することです。しかし、プロキシは完全に透過的なわけではありません。たとえば、プロキシでは、オブジェクトのインスタンス変数の設定や取得を直接行うことはできません。
リモートレシーバは一般的に匿名です。そのクラスはリモートアプリケーション内に隠蔽されています。送信側アプリケーションは、アプリケーションの設計や使用するクラスを知っている必要はありません。また、送信側で同じクラスを使用する必要もありません。知っている必要があるのは、リモートオブジェクトがどのようなメッセージに応答するかということだけです。
そのため、リモートメッセージを受信するように指定されたオブジェクトは、形式プロトコルとしてインターフェイスを公開します。送信側および受信側アプリケーションの双方でプロトコルを宣言します。どちらも同じプロトコル宣言をインポートします。リモートオブジェクトはプロトコルに準拠する必要があるため、受信側アプリケーションでそのプロトコルを宣言します。送信側アプリケーションでプロトコルを宣言するのは、送信するメッセージに関してコンパイラに通知するためであり、また、conformsToProtocol:メソッドと@protocol()ディレクティブを使用してリモートレシーバをテストするためです。送信側アプリケーションは、そのプロトコルにいかなるメソッドも実装する必要はありません。プロトコルを宣言するのは、単にリモートレシーバに対してメッセージを開始するからです。
NSProxyやNSConnectionクラスなど、分散オブジェクトアーキテクチャについては、Foundationフレームワークリファレンスの『Distributed Objects Programming Topics』を参照してください。
リモートメッセージングは、プログラム設計に多くの魅力をもたらす可能性があるだけでなく、Objective-C言語のいくつかの興味深い問題も提起します。問題の大半は、リモートメッセージングの効率と、2つのタスクが互いに通信している間に保っているべき分離の程度に関するものです。
プログラマがリモートメッセージの目的に関する明示的な指示を与えられるように、Objective-Cでは、形式プロトコル内でメソッドを宣言する際に使用できる次の6つの型修飾子を定義しています。
oneway
in
out
inout
bycopy
byref
これらの修飾子の使用は形式プロトコルに限定され、クラスおよびカテゴリ宣言に使用することはできません。ただし、クラスまたはカテゴリでプロトコルを採用する場合、プロトコルメソッドの実装では、対応するメソッドの宣言に使用されている修飾子を使用することができます。
以降のセクションでは、これらの修飾子の使いかたについて説明します。
まず、ごく単純な戻り値を持つメソッドについて考えてみます。
- (BOOL)canDance; |
canDanceメッセージを同じアプリケーションのレシーバに送信すると、このメソッドが呼び出され、戻り値が送信側に直接提供されます。しかし、レシーバがリモートアプリケーションにある場合は、2つの基礎的なメッセージが必要になります。1つはリモートオブジェクトを取得してメソッドを呼び出すメッセージで、もう1つはリモート計算の結果を返送するメッセージです。これについては、次の図に示します。
ほとんどのリモートメッセージは基本的に、次に説明するような、双方向(または「往復」)のリモートプロシージャ呼び出し(RPC)です。送信側アプリケーションは、受信側アプリケーションがメソッドを呼び出して処理を終了し、終了したことを示すメッセージを、要求された情報があればそれらとともに返送するまで待機します。情報が返されなかったとしても、レシーバが終了するのを待機するため、2つの通信アプリケーションを調整して、両者の「同期」を保てるという利点があります。そのため、往復メッセージは、同期メッセージとも呼ばれます。同期メッセージがデフォルトです。
しかし、応答を待機することは必ずしも必要なわけではなく、得策でないこともあります。場合によっては、リモートメッセージを単純に送信して戻るだけでよいこともあります。そして、レシーバが可能なときにタスクに取り掛かるのに任せます。その間、送信側は他のことを続けられます。Objective-Cは戻り型修飾子onewayを指定して、メソッドが非同期メッセージ専用に使用されることを示します。
- (oneway void)waltzAtWill; |
onewayは(constのような)型修飾子であり、oneway floatあるいはoneway idのように、特定の型名と組み合わせて使用できますが、唯一意味のある組み合わせはoneway voidだけです。非同期メッセージは、有効な戻り値を持つことができません。
次に、ポインタ引数を受け取るメソッドについて考えます。ポインタは、参照によってレシーバに情報を渡すために使用できます。呼び出されたメソッドは、渡されたアドレスに格納されているものを参照します。
- setTune:(struct tune *)aSong |
{ |
tune = *aSong; |
... |
} |
また、同じ種類の引数を使用して、参照によって情報を返すこともできます。メソッドはポインタから、メッセージによって要求された情報の格納場所を知ることができます。
- getTune:(struct tune *)theSong |
{ |
... |
*theSong = tune; |
} |
ポインタの使いかたによって、リモートメッセージの処理方法が変わります。いずれの場合も、ポインタをそのままの状態でリモートオブジェクトに渡すことはできません。ポインタは、送信側のアドレス空間内のメモリ領域を指しているため、リモートレシーバのアドレス空間では意味を持ちません。リモートメッセージングのランタイムシステムは、多少の調整を行う必要があります。
参照によって情報を渡すために引数を使用する場合、ランタイムシステムはポインタをたどり、ポインタが指す値をリモートアプリケーションに送信して、その値をアプリケーションのローカルアドレスに格納し、そのアドレスをリモートレシーバに渡す必要があります。
一方、参照によって情報を返すためにポインタを使用する場合は、ポインタが指す値を他のアプリケーションに送信する必要はありません。その代わりに、他のアプリケーションからの値の返送を受け、ポインタが示す場所に書き込む必要があります。
初めのケースでは、情報を往路で渡しています。次のケースでは、情報を復路で返しています。このような場合、リモートメッセージングのためのランタイムシステム側では動作が大きく異なるため、Objective-Cはプログラマの意図を明示できる型修飾子を提供します。
型修飾子inは、メッセージに情報を含めて渡すことを示します。
- setTune:(in struct tune *)aSong; |
修飾子outは、参照によって情報を返すために引数を使用することを示します。
- getTune:(out struct tune *)theSong; |
第3の修飾子inoutは、情報の提供と返送のために引数を使用することを示します。
- adjustTune:(inout struct tune *)aSong; |
Cocoa分散オブジェクトシステムでは、inがデフォルトであるconstとして宣言されたポインタ引数を除き、すべてのポインタ引数のデフォルト修飾子がinoutとなります。inoutは最も安全な条件ですが、双方向で情報を渡す必要があるため、最も時間がかかるものでもあります。値(ポインタ以外)によって渡される引数にとって、意味のある唯一の修飾子はinです。inはどのような種類の引数でも使用できますが、outとinoutはポインタに対してのみ意味があります。
C言語では、複合値を表すためにポインタを使用する場合もあります。たとえば、文字列は文字ポインタ(char *)として表します。表記と実装においては、間接のレベルが存在しますが、概念上はありません。概念的には、文字列はそれ自体がエンティティであり、他のものへのポインタではありません。
このような場合には、分散オブジェクトシステムはポインタを自動的にたどり、ポインタが指すものを値渡しのように渡します。したがって、outおよびinout修飾子は、単純な文字ポインタでは意味をなしません。参照によって文字列を渡したり、返したりすると、リモートメッセージに間接のレベルが生まれます。
- getTuneTitle:(out char **)theTitle; |
同じことがオブジェクトにも当てはまります。
- adjustRectangle:(inout Rectangle **)theRect; |
これらの規則はコンパイラによってではなく、実行時に適用されます。
最後に、オブジェクトを引数として受け取るメソッドについて考えます。
- danceWith:(id)aPartner; |
danceWith:メッセージは、オブジェクトidをレシーバに渡します。送信側と受信側が同じアプリケーションにある場合は、どちらも同じaPartnerオブジェクトを参照することができます。
受信側がリモートアプリケーションにあったとしても、これは変わりません。ただし、受信側はプロキシを通してオブジェクトを参照する必要があります(オブジェクトがそのアドレス空間に存在しないため)。danceWith:がリモートレシーバに提供するポインタは、実際にはプロキシへのポインタです。プロキシに送信するメッセージは、接続を介して実際のオブジェクトへ渡され、返される情報があれば、それらはリモートアプリケーションに返されます。
プロキシがあまりにも非効率的である場合、オブジェクトのコピーをリモートプロセスに送信して、そのアドレス空間で直接対話できるようにしたほうがよい場合があります。このような意図を示す方法をプログラマに提供するために、Objective-Cにはbycopy型修飾子が用意されています。
- danceWith:(bycopy id)aClone; |
bycopyは戻り値にも使用できます。
- (bycopy)dancer; |
この型修飾子をoutと併用すると、参照が返すオブジェクトをプロキシの形で提供するのではなく、コピーするように指示できます。
- getDancer:(bycopy out id *)theDancer; |
bycopyは、クラスによっては非常に有用であるため(たとえば、他のオブジェクトのコレクションを含むことを目的としたクラスなど)、そのようなクラスでは、通常の参照の代わりにコピーをリモートレシーバに送信するように記述されている場合があります。しかし、このような動作をbyrefでオーバーライドして、メソッドから返されるメソッドやオブジェクトに渡すオブジェクトを、参照で渡したり返すように指定することもできます。Objective-Cオブジェクトのほどんどでは参照渡しがデフォルトの動作であるため、byrefキーワードを実際に使用することはほとんどありません。
bycopyまたはbyrefで変更する意味のある唯一の型はオブジェクトです。idで動的に型定義するか、クラス名で静的に型定義するかは関係ありません。
bycopyとbyrefはクラスおよびカテゴリ宣言内では使用できませんが、形式プロトコル内では使用できます。たとえば、形式プロトコルfooは次のように記述できます。
@Protocol foo |
- (bycopy)array; |
@end |
以降、クラスまたはカテゴリでプロトコルfooを採用することができます。これによって、プロトコルに記述されたメソッドがオブジェクトをどのように渡したり返したりするべきかを示す「ヒント」を提供するプロトコルの構築が可能になります。
| < 前ページ次ページ > |
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 |