【コラム】

ダイナミックObjective-C

20 メソッドとは何か(3) - メソッドの型を読み解く

    木下誠  [2006/01/11]

    今回は前回に引き続きメソッド調査の話をしよう。前回までで、メソッドの名前を得ることはできた。次は引数の型の調査だ。

    引数の型のエンコーディング

    前回、メソッドの一覧表示をする関数を紹介したが、その時に出てきた"@8@0:4"といった謎の文字列。これは何かと言えば、引数の型をエンコードして文字列で表したものだ。返り値の型も含まれている。

    この文字列の読み解き方を説明しよう。まず、基本となる記述は、「引数の型+数値」というペアだ。引数の型、つまりint型やchar型といったもの、は、1文字の記号に置き換えられている。その後に、引数のバイト数を示す、数値が来る。このペアの繰り返しが、続くことになる。このエンコードは、まずメソッドの返り値の型から始まる。そして、2番目の文字は第1引数、3番目の文字は第2引数、といった感じで続いていく。

    例として、さきほどの"@8@0:4"という文字列を考えてみよう。「引数の型+数値」のペアで分解してみる。すると、"@8" + "@0" + ":4"、という、3つのペアで構成されている。これらのうち最初のもの、つまり"@8"が、返り値の型。そして、第1引数が"@0"、第2引数が":4"、ということになる。

    エンコーディングに使われる文字

    では、型のエンコードに使われる文字を紹介しよう。以下の文字が使われている。

    c char
    i int
    s short
    l long
    q long long
    C unsigned char
    I unsigned int
    S unsigned short
    L unsigned long
    Q unsigned long long
    f float
    d double
    B C++のbool、またはC99の_Bool
    v void
    * 文字列(char*)
    @ オブジェクト (静的に型定義されているもの、または id として型定義されているもの)
    # クラスオブジェクト(Class)
    : メソッドセレクタ(SEL)

    [配列型] 配列
    {名前=型...} 構造体
    (型...) 共用体
    bnum num ビットのビットフィールド
    ^型 型へのポインタ
    ? 不明な型(このコードは特に関数ポインタに使用される)

    r const
    n in
    N inout
    o out
    O bycopy
    R byref
    V oneway

    これで、すべてだ。C言語のすべての組み込み型のサポートに加えて、Objective-C特有の型も存在することが分かる。オブジェクトを表す"@"や、セレクタを表す":"などが、それだ。

    また、後半にあるconstやinといったものは、Objective-Cの、引数への修飾子だ。定数宣言や、値渡し、参照渡しの明示化などが含まれている。これらは、積極的に使われることは少ないが、言語仕様には含まれている。

    引数のバイト数とは?

    型を表す文字の次に来るのは、引数のバイト数を表す数値ということになっている。定義によれば、これは実行時に引数をスタック上に配置する時に、必要なバイト数、ということになっている。

    だが、少し考えれば分かるとは思うが、実際に割り当てられるサイズはCPUに強く依存する。現在は、この数値は過去との互換性のために残されているだけのようである。Mac OS Xのランタイムでは、この数値は無視されてしまう。

    メソッドの実装と暗黙の引数

    このエンコードした文字列は、メソッドの引数を表すものではあるが、正確に言うならば、「メソッドの実装の関数」の引数である。

    メソッドと、その実装の関数に、引数の違いはあるのか? 実は、あるのだ。詳しくは、メソッドの実装の回で説明するが、実装関数は暗黙的に2つの引数をとることになっている。1つ目は、メソッドを実行するオブジェクト。2つ目は、メソッドのセレクタになる。メソッドの引数は、その後に来ることになる。

    だから、エンコードされた引数の、第1引数と第2引数は、常に暗黙に付加されたものということになる。その型は、オブジェクト型の"@"と、セレクタ型の":"だ。

    実際に読み解く

    これで、エンコードされた文字列から、メソッドの引数を読み解く準備が整った。実際に解析してみよう。

    まず、initWithString:というメソッドを考えてみる。宣言は次の通りだ。

    - (id)initWithString:(NSString*)string

    このメソッドの返り値と引数の型をエンコードすると、次のようになる。

    @12@0:4@8

    これを読み解いてみよう。まず、数字は無視して、型を並べてみる。すると、"@@:@"という、4つの型が浮かび上がる。このうち、最初の1つは、返り値の型で、id型。2つ目と3つ目は、暗黙の引数。4つ目がメソッドの型、これもid型ということになる。

    整理すれば、"@12@0:4@8"というエンコードから、「返り値がid型で、id型の引き数を1つとるメソッド」ということが分かる。うまく、上のメソッド宣言と合致している。

    もう1つ例を挙げよう。次の文字列を読み解いてみる。

    @16@0:4r*8I12

    同じように読解すれば、"@@:r*I"となり、「返り値がid型で、引数として定数文字列(r*)、unsigned int(I)をとる」メソッド、ということになる。ちなみに、これは次のメソッドをエンコードしたものである。

    - (id)initWithCString:(const char*)nullTerminatedCString encoding:(NSStringEncoding)encoding

    エンコードされた文字列の解析

    これで、クラス構造体が持つ、メソッドの引数を取り出すことができた。前回のメソッド名と合わせれば、完全なメソッドの情報ができあがる。

    新着記事

    特設サイトの情報

      人気記事

      一覧

        イチオシ記事

        新着記事

        特別企画

        マイナビニュースマガジン