Objective-Cライブラリーとの接続

iMopsでは、CocoaのGUIエレメントや、CoreFoundationライブラリーを利用するために、特別な方式を定めています。ここでは、その方式を説明します。Objective(オブジェクティブ)-CやCocoaオブジェクトの使い方については説明しませんが、利用に際してはAppleから提供されているドキュメントなどで、十分に理解する必要があります。

まず、大まかに言って、Objective-C系のライブラリーで利用できるのは、Objective-Cのクラス、インスタンス、メソッドがあります。関数として含まれているものは、フレームワークコールの方式で呼び出すことができるので、ここでは説明しません。

ここでの説明だけからでは実際の使い方はわかりにくいと思われます。iMops付属の source/CocoaClassesフォルダの中にあるソースコードファイルに実際の利用例があるので、サンプルコードとなると思います。

Objective C クラス

Objective-Cのインスタンスを利用するには、ソースコード内でクラスを宣言することが必要です。一般的書式は次のようになります。
OBJC_CLASS クラス名 "真のObj-Cクラス名"
つまり、まず、ObjC_Classと書いて、その後に、iMops内でのそのクラスの名前を書きます。これは任意の名前が利用できますが、元のObjective-Cのクラス名がわかるような名前にしないと、後で混乱するかもしれません。システム内では、Objective-Cのクラス名の後にCLASSを付加するという基本方針で命名しています。そして、最後に、二重引用符で囲んで、Objective-Cでの本当のクラス名を書きます。最後のクラス名は、大文字小文字を正確に書き分けなければなりません。
例えば、NSWindowクラスを宣言する場合には、
ObjC_Class NSWindowClass "NSWindow"
とします。そうすると、NSWindowClassは、CocoaのNSWindowクラスのクラス リファレンスを返すワードになります。

Objective C セレクター

セレクターをオブジェクトに渡すことによって、オブジェクトは対応するメソッドを呼び出して、必要な処理を行います。上で宣言したクラスも、セレクターを受け取ってインスタンスを生成したりするという点で、オブジェクトの一種です。
Objective-Cセレクターも、宣言することによって利用可能となります。構文は、クラスの場合と並行しています。
ObjC_Selector セレクター名 "真のObj-Cセレクター名"
セレクター名は任意ですが、元のObjective-Cのメソッドがわかるような名前にした方が良いでしょう。二重引用符で囲った部分は、Objective-Cのメソッド名を、大文字小文字の別も含めて、正確に書かないといけません。メソッド名は、Apple社のリファレンス・ドキュメントに表示されているメソッド名です。つまり、引数毎に分け書きするソースコード内のメソッド名ではなくて、引数分のコロンでつながったひと続きの文字列です。例えば、
ObjC_SELECTOR ReadFromURLSel "readFromURL:options:documentAttributes:error:"
などのように書きます。上のセレクターに対応するメソッドは、引数を4個とります。iMopsコード内で用いられるセレクター名は、引数の個数の情報が落ちてしまいますが、なるべく短くしつつ、機能はわかるような名前にし、加えて、最後にSelとつけるようにしています(カッコよくありませんが、他のワードとの区別できるように、何らかの工夫は必要となるので、当面はこの規約で)。
宣言の仕方からもわかるように、Objective-Cのメソッドセレクターは、クラスとは独立の存在です。内部的には単なる文字列として扱われているようです。結果として、まったく同じ名前のメソッドがあるなら、同じセレクターを用いて、違うクラスの、違うメソッドを呼び出すことができます。これは普通はポリモーフィズムと呼ばれ、何かオブジェクト指向の特別な利点のように宣伝されます。けれども、実際には、異なるクラスでまったく同じ名前のメソッドというのは、極く原始的なもの以外は、ほとんど定義されていません。

Objective-Cメソッドを呼び出すには、このセレクター(上の例でいうと、ReadFromURLSel というワード)を束縛(バインド)関数に引数として渡します。具体的な構文は、インスタンス生成の中で説明します。

Objective C インスタンス

クラスだけだと、あまりたくさんの機能は果たせないのであって、それをインスタンス化して、オブジェクトを生成することで初めて、ライブラリーの多様な機能が利用できるようになります。具体的なインスタンスの動作は、インスタンスメソッドと呼ばれ、これが、ライブラリ関数のような機能を果たします。

汎用コンストラクター

まず、中身が何もないインスタンスを生成するための、汎用のメソッドは、ワードの形にして定義してあります。コンストラクターなどと言います。関数名は、例によってあまり格好良くありませんが、
NewObjC ( ^class -- ^instance )
これは、Objective-Cのクラスレファレンスを引数として実行すると、ヒープ上に生成されたインスタンスのレファレンスがスタック上に返される関数です。返された値は、MopsのZVARクラスオブジェクトか、ZValueまたはZVariableなど、1セル(8バイト)長の変数に格納しておきます。見失ってしまうと、メモリーリークの原因になるので注意してください。例えば、
ZVariable Winref
NSWindowClass NewObjC Winref z!
とすれば、変数WinrefにNSWindowの初期化されたインスタンスの参照(ポインター)が格納されます。

通常は、Objective-Cインスタンスは、Mopsオブジェクトの中にインスタンス変数として格納して、Objective-Cインスタンスの機能をMopsオブジェクトの機能とすることで、拡張していくことになると思います(Mops sourceのCocoaClassesフォルダの中にあるファイルを参照)。

ただし、上のコンストラクターは、初期値が現実的にあまり合理的ではないようです。そこで状況に適した初期化が必要となります。必要な初期値ごとにメソッドで追加していくこともできますが、初めから必要な範囲で初期値を与えつつインスタンスを生成するためのコンストラクターメソッドが、各クラスに定義されていることがほとんどです。普通は後者を用いることになるので、汎用コンストラクターの出番は、あまりないと思います。

汎用デストラクター

一旦生成したObjective-Cインスタンスを、もう使わないというときの汎用メソッド(デストラクター)も、次のワードとして定義してあります。
ReleaseObjC ( ^instance -- )
これは、Objective-Cインスタンスのレファレンスカウントをひとつ減らすメソッドです。すぐに廃棄はされませんが、Objective-Cのガベージコレクターにチェックされて回収されます。Objective-CクラスをMopsクラスで包み込むときは、Release:メソッドの中に、この汎用メソッドか、適宜、クラスに応じた適切なデストラクターメソッド組み込んでおくべきことになります。通常は、この汎用デストラクターを使う機会は、ほとんどないと思います。というのは、各クラスが、そのクラスに適したデストラクターメソッドを持っているので、適切に解放するには、そちらを使うべきだからです。

メッセージバインド関数

Objective-Cのオブジェクト指向実装は、低レベルでは、セレクターとクラスデータから適切なメソッドを探して実行するC関数を用いることで実現されています。Mopsでは、この関数をあからさまに用いる方式を採用しました。Objective-Cのコードでも、実際にコンパイルされるコードは、実は同じです。

メッセージバインド関数は、メタな動作をする関数ではありますが、引数の個数を自動判別するには、それなりの構文が必要になって面倒なので、引数の種類・個数と戻り値の種類・個数ごとに、別々の関数として定義する、という方式にしました。そして、規約として関数名でイン-アウトの個数がわかるようにしてあります。

例えば、呼び出されるべきメソッドが、引数は二つの整数、戻り値は一つの整数、というものである場合、
2-1MsgSend
というバインド関数を用います。構文は、
オブジェクト セレクターSel パラメター1パラメター2 2-1MsgSend
という順になります。つまり、最初にメッセージを受けるべきオブジェクト、次にセレクター、そして必要な個数のパラメターを引数の順番において、バインド関数を呼ぶ、ということです。戻り値はスタックに積まれます。オブジェクトとは、クラスメソッドの場合はクラス リファレンス、インスタンスメソッドの場合はインスタンス リファレンスを意味します(要は、ポインターです)。

数の種類としては、浮動小数点数でないものは全て整数と考えます。浮動小数点数があるときには、個数の前に%をつけるという規約にしています。例えば、整数引数が2個、小数の引数が1個、戻り値が小数であるような場合には、ややわかりにくいですが、
2%1-%1MsgSend
のような名前になります。

かなり多くの組み合わせについて、バインド関数はすでに定義されていますが、全ての場合を尽くしているわけではありません。もしも、呼び出したいメソッドに対応するバインド関数がまだ定義されていない場合は、次のような方式で宣言することで、新たに定義することができます。例えば、整数引数を4個、小数引数を3個、整数の戻り値がひとつ、というメソッドをバインドしたい場合は、
ObjCMethod 4%3-1MsgSend { ip1 ip2 ip3 ip4 %fp1 %fp2 %fp3 -- res }
のように宣言して、このバインド関数4%3-1MsgSendを用います。
最初のObjCMethodが宣言、4%3-1MsgSendは、ここで定義されたバインド関数の名前(実際上は任意)、そして、中カッコ{}内は、パラメターの種類の宣言です。中カッコ内の記述によってパラメターの個数と種類をMopsシステムに知らせますので、この部分が一番重要です。書き方は、名前付き引数の場合と同じ規約で、初めが%は浮動小数点数、そうでなければ整数と判断されます。小数整数の別以外の情報は必要ありませんので、なんでもいいので、個数がわかりやすいパラメター表示名をつけてください。
一度宣言されれば、それ以降は普通にワードとして利用できます。

Objective-C メッセージバインド

上でも述べましたが、Objective-Cのオブジェクトにメッセージを送ってメソッドを起動する場合には、パラメター数が対応するバインド関数を用います。例えば、整数引数が1つで、整数戻り値が一つのメソッドの場合、構文は
オブジェクト セレクタ パラメタ1  1-1MsgSend
という順になります。つまり、オブジェクト リファレンスとセレクタ リファレンス(どちらもメモリーポインター)が、いわば追加的パラメターとして、整数スタックの初めに置かれ、その後に関数としてのパラメターを置くことになります。

オブジェクトリファレンスは、クラスメソッドの場合はクラス、インスタンスメソッドの場合はインスタンスとなります。クラスをオブジェクトにする場合には、ObjC_Class宣言で定義したクラス名をワードとして利用できます。インスタンスリファレンスは、なんらかのNewメソッドを経由して得たものを用います。通常は、変数やMops変数オブジェクトに格納しておき、その値を取り出して利用します。

セレクタリファレンスは、上のObjC_Selector宣言で定義した、セレクタ名(最後にSelを付加するという規約にしたやつ)をワードとして用います。セレクタ名からパラメターの種類と数がわかるような規約(Objective-Cはそうなっている)で命名するようにしてもいいのですが、システム内ですでに利用されているセレクタ名はそうなっていません。利用するときには、Mopsソース内での定義部分をlocateで参照するか、Appleのドキュメントを参照するかして、正しい引数-戻り値の種類(整数か浮動小数点数か)と数を知っておく必要が有ります。

なお、整数値は整数スタックを経由しますが、浮動小数点数パラメターは、浮動小数点数スタックを経由するので、パラメターの順番は、整数だけの間/浮動小数点数だけの間 では、仕様の順番を守る必要がありますが、整数と浮動小数点数の間では、とくに順番に意味はありません。この場合、オブジェクトとセレクターは整数値に入りますので、整数の引数の前に、同順でスタックに置かれる必要があります。しかし、パラメターとしてのオブジェクト・セレクターは、どちらも浮動小数点数パラメターとは干渉しません。したがって、例えば、まず浮動小数点数のパラメターを置いてから、オブジェクトリファレンスを整数スタックにおいても構いません。また、オブジェクトリファレンス、セレクターも含めて、すべて一旦スタックに置かれる値であるので、dupやswap、rotなどのスタック操作ワードが利用できます。つまり、上の「構文規則」は、ソースコードの表記順としての「構文規則」ではなくて、スタック上での値の順序としての規則、というべきことになります。スタック上に値を揃えて、最後にメソッドバインド関数を呼ぶ、という順になります。

なお、Objective-Cのメソッドのパラメターや戻り値について、ドキュメント上での変数型と実際の実装方法(コンパイルされるコード)とが一致していないものがあります。Xcodeでなら、コンパイラが処理してくれる問題ですが、Mopsから呼ぶときにはこちらで対応する必要があります。具体的には、ほぼ、構造体であるパラメターや戻り値をどうするかという問題につきます。この問題は、コードを書くという点では比較的初歩的なところから出てくる問題ですが、機械に近い話であるため、やや長たらしい説明が必要となるので、別の項目とすることとします。

最後に、Objetive-Cのクラスやセレクター、メソッドバインド関数は、すでに定義されているものは、新たに自分で宣言する必要はありません。システム内ですでに定義されているObjective-Cのクラス、セレクター、バインド関数の名前は、iMops/sourceフォルダ内の、"PredefinedObjC"という名前のファイルに書いてあります。ここにあるものは、自分で定義する必要は無く、その名を用いて利用できます。

関連項目


最終更新:2019年11月20日 21:49