【コラム】

ダイナミックObjective-C

17 クラスとは何か(4) - Objective-Cにおけるオブジェクトとは何か?

    木下誠  [2005/12/07]

    前回までは、Objective-Cで、クラスはどのように実装されているのかを探っていた。続いて、オブジェクトの実装に話を移そう。クラスをもとにして、どのようにインスタンス化を行うのか、ソースコードレベルで確認しよう。

    オブジェクトとは何か

    この連載でも何度か述べているように、Objective-Cにおいて、すべてのオブジェクトはid型で表すことができる。そこで、まずid型の定義を調べることから始めよう。Objective-Cランタイムのヘッダファイル、obc-api.hで見つけることができる。

    objc-api.h
    typedef struct objc_object {
        class isa;
    } *id;

    ここから、id型とは、構造体objc_objectのポインタ型であることが分かる。この構造体は、フィールドを1つだけ持っている。class型であるisaだ。

    isaは、このオブジェクトの、クラス定義になる。これにより、あるインスタンスオブジェクトからは、いつでもそのクラス定義、つまり自身のメタ情報、にアクセスできることになる。これが、objective-cの、高い動的特性を実現するためのエントリーポイントになっている。

    実際にインスタンスオブジェクトを確保すると、このisaの下にインスタンス変数のためのメモリが確保されることになる。つまり、id型で参照するメモリ領域の大きさは可変長だ。必要になるインスタンス変数のサイズは、クラス定義、つまりisa、から取得できる。objc_class構造体の、instance_sizeフィールドだ。

    objc_object構造体と、その他の構造体の関係

    オブジェクトの確保

    実際に、ランタイムのソースコードから、インスタンスオブジェクトを作成する箇所を調べてみよう。Objective-Cでは、インスタンス化はallocというクラスメソッドで行う。allocの実際の処理を行うのは、ランタイムAPIの、class_createInstanceという関数だ。

    id class_createInstance(Class theClass, unsigned additionalByteCount);

    最初の引数として渡されるのが、クラス定義であるtheClass。2つ目の引数は、インスタンス変数以外に、余分なメモリを確保したい時に指定する、additionalByteCountだ。

    この関数の実装は、objc-class.mというファイルにある。実際に挙動を追いかけてみると、最終的に_internal_class_createInstanceFromZoneという関数を呼び出す。ここで、オブジェクトのためのメモリ確保が行われる。

    // クラスをインスタンス化する
    static id _internal_class_createInstanceFromZone (Class    aClass,
                                                     unsigned nIvarBytes,
                                                     void * z)
    {
        // インスタンスオブジェクトのための変数
        id obj;
        // オブジェクトのサイズ
        register unsigned byteCount;
        
        ...
        
        // オブジェクトのサイズを求める
        // クラス定義からinstance_sizeを取り出し、余分なメモリを確保する時はそれも加える
        byteCount = ((struct objc_class *) aClass)->instance_size + nIvarBytes;
        
        // オブジェクトのためのメモリを確保する
        obj = (id) malloc_zone_calloc (z, 1, byteCount);
        if (!obj)
        {
            // エラー処理
            ...
        }

        // isaポインタを設定する
        // 引数として渡ってきた、クラス定義を設定する
        obj->isa = aClass;
        
        ...
    }

    処理の流れとしては、まず、オブジェクトのサイズを求める。次に、メモリを確保する。そして、isaにクラス定義を設定する。基本的にはこれだけだ。これで、オブジェクトとして動作する。この後に、initメソッドなどで、インスタンス変数の初期化などが行われるだろう。

    オブジェクトの条件

    このように、オブジェクトとは何か、ということを実装面から見ると、「クラス定義であるisaフィールドで始まり、インスタンス変数のためのメモリ領域を持つもの」と、なる。

    ここで、前回まで説明した、Class型を思い出してみよう。Class型、つまりobjc_class構造体も、メタクラスであるisaで始まっていた。つまり、id型と同じであり、オブジェクトとしての条件を満たしている。「クラスもオブジェクトである」というObjective-Cの言語仕様は、「id型も、Class型も、クラス定義であるisaで始まる」と、実装面からも確認できるということだ。ちなみに、クラスオブジェクトにはクラス変数は存在しないので、objc_class構造体に変数用の可変長領域は必要ない。

    では、構造体で先頭にisaがあれば、何でもオブジェクトなのか? まさに、その通りである。たとえば、C言語で構造体を作り、その先頭にisaとしてクラス定義へのポインタを置けば、それでもうObjective-Cのオブジェクトになる。

    実は、Mac OS Xでは、実際にこの仕組みを利用して、C言語とObjective-Cのシームレスな接続を実現している。この環境は、Core Foundationと呼ばれる。Mac OS Xでは、CocoaとCarbonという二大フレームワークが存在するが、Objective-CベースのCocoaと、C言語ベースのCarbonの間で、Core Foundationを利用してオブジェクトをそのまま受け渡すことができる。これは、Objective-CがC言語ベースである、ということからくる、大きな利点を活用したものと言えるだろう。

    Core Foundationについては、後日、項を改めて詳しく説明しよう。

    新着記事

    特設サイトの情報

      人気記事

      一覧

        イチオシ記事

        新着記事

        特別企画

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