【コラム】

ダイナミックObjective-C

16 クラスとは何か(3) - メタクラスと親クラス

    木下誠  [2005/11/30]

    今回は、クラス構造体のうち、クラス情報のフィールドを取り上げよう。isaとsuper_classだ。

    メタクラス

    クラス構造体には、isaとsuper_classというフィールドがある。どちらも、クラス構造体(struct objc_class)へのポインタだ。super_classの正体は予想がつくと思う。このクラスの親クラスへのポインタだ。では、isaとは何か?

    isaの意味は、「このオブジェクトの定義となるオブジェクト」ということになる。たとえば、オブジェクトAのisaがオブジェクトBである場合、オブジェクトAの定義はオブジェクトB。つまり、"ObjectA is an ObjectB"と、考えればいい。

    これでは概念的すぎるので、もっと実践的に考えてみよう。まず、isaは、クラスとインスタンスオブジェクトの間でも使われる。後日説明するが、インスタンスオブジェクトもisaフィールドを持っているのだ。この場合、このフィールドは自分自身が属するクラスを指すことになる。たとえば、NSStringのインスタンスオブジェクトのisaは、NSStringのクラスになる。

    そして、そのNSStringクラスだが、これもisaフィールドを持っている。上の考え方によれば、ここには「NSStringクラスの定義となるオブジェクト」が入ることになる。クラスの定義となるオブジェクトとは何か?

    Objective-Cでは、クラスもオブジェクトとして扱う。オブジェクトとは、その定義より作成されて、メッセージを処理することができるもの、だ。つまり、インスタンスオブジェクトがその定義であるクラスより作られるように、クラスオブジェクトもその定義が必要になる。これをクラスのクラスという意味で、メタクラスと呼ぶ。NSStringクラスのisaフィールドには、NSStringメタクラスへのポインタが入るのだ 。

    インスタンスとクラスとメタクラス

    メタクラスの性質

    メタクラスは、クラスのためのクラスだ。メタクラスとクラスの間の関係は、クラスとインスタンスの間の関係と似たものになる。

    たとえば、NSStringクラスは、NSStringインスタンスのためのインスタンスメソッドを管理する。それと同様に、NSStringメタクラスは、クラスメソッドを管理することになる。インスタンスメソッドとクラスメソッドは、実装レベルで見れば、管理するクラス構造体が違っている。

    ただし、インスタンス変数は同じようには扱われない。クラスにはインスタンス変数の定義があるが、メタクラスにはそれに対応するものがない。Objective-Cでは、クラス変数のための文法がないためである。これは、しばしば、Objective-Cの文法に一貫性がないとして指摘されるところである。実践的には、実装ファイルでstatic変数を宣言することで、クラス変数として利用する。

    クラスとメタクラスは、ともにクラス構造体、つまりstruct objc_classになる。そこで、区別をつけるために、infoフィールドが使われる。infoフィールドは、その構造体の情報を表すフラグを格納する。その構造体がクラスのためのものであればCLS_CLASS (0x1L)の、メタクラスであればCLS_META (0x2L)のフラグが立つ。

    メタクラスと親クラス

    メタクラスにも、isaとsuper_classフィールドがある。これらは何を指すのであろうか?

    まず確認しておくが、メタクラスはオブジェクトではない。メタメタクラスは存在しないのである。したがって、メタクラスのisaフィールドは、意味をなさないと考えることができる。一応、Mac OS Xの実装では、このフィールドにはNSObjectメタクラスへのポインタが入っている。これは、便宜的なものではないだろうか。

    super_classの方には、メタクラスの親メタクラスへのポインタが入る。NSStringメタクラスならば、NSObjectメタクラスになる。親クラスが指定されているということは、メタクラスでも、継承を用いたメソッド呼び出しが行われるということだ。たとえば、NSStringのクラスメソッドを呼び出すと、まずNSStringメタクラスにメソッドを探しにいき、そこになければ親クラスであるNSObjectメタクラスで探すことになる。インスタンスメソッドでの呼び出し方と同じだ。

    では、ルートクラスのメタクラスである、NSObjectメタクラスの親クラスはどうなっているのか?実は、NSObjectクラスになっている。少しややこしいことになるが、すべてのメタクラスの継承構造をたどっていくと、NSObjectメタクラスを経由して、最終的にNSObjectクラスにたどり着くことになる。つまり、メタクラスのルートクラスも、NSObjectクラスなのだ。

    これでどうなるかというと、クラスメソッドの探索は、最終的にNSObjectクラスに行き着く。ということは、クラスオブジェクトで、すべてのNSObjectのインスタンスメソッドが使える、ということになる。

    メタクラスと親クラス

    メタクラスの利点

    メタラクスを導入する利点は、クラスもオブジェクトとみなせること。さらに、プログラマの立場からすると、クラスオブジェクトでもNSObjectのインスタンスメソッドが使えることだ。

    NSObjectのインスタンスメソッドには、同一性をチェックするisEqual:、クラスをチェックするisKindOfClass:、ハッシュ値を得るhash、セレクタを指定して実行するperformSelector:などがあるが、これらがすべて使える。つまり、コンテナクラスであるNSArrayやNSDictionaryで、そのままクラスオブジェクトを扱える。実際にクラスを使ってプログラミングする際に、非常に有用だ。

    が、実際には、Objective-Cでのプログラミングの際に、メタクラスの存在を意識することは少ない。Smalltalkなどの環境と比べると、その利用機会は控えめであろう。

    新着記事

    特設サイトの情報

      人気記事

      一覧

        イチオシ記事

        新着記事

        特別企画

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