home link download back number special issue

HMDT - Special Issue / CoreFoundation の秘密 / Base Services - 型の動的判別


- Base Services -
型の動的判別

型の動的判別

CoreFoundation ではオブジェクトの型を動的に調べることができるんだ。型情報は CFTypeID 型で受ける。任意のオブジェクトの型を調べるときは CFGetTypeID() で調べるんだ。

CoreFoundation/CFBase.h
CF_EXPORT
CFTypeID CFGetTypeID(CFTypeRef cf);

これで、型が返ってくる。ただし、型の値は固定ではないので注意。バージョンやランタイムで変わる可能性があるんだ。

これに対して、ある特定のオブジェクトの型を調べたいときは、CFTypeGetTypeID() っていう形の関数がそれぞれのオブジェクトに用意されてるんで、それを使うんだ。たとえば、CFString の型を得るために、CFStringGetTypeID() っていう関数が用意されてる。

CoreFoundation/String.subproj/CFString.h
CF_EXPORT
CFTypeID CFStringGetTypeID(void);

こうやって得られる型を比較することで、オブジェクトの型を決定することができるんだ。型を決定するコードを、Objective-C と比べてみよう。

(sample)
/* --- Objective-C --- */

id object;

if ([object isKindOfClass:[NSString class]]) {
    // object is NSString class
}

/* --- CoreFoundation --- */

CFTypeRef object;

if (CFGetTypeID(object) == CFGetStringTypeID()) {
    // object is CFString
}

っていう感じになるよ。これで多態性を利用したプログラムが書けることになるね。

実装を探る

それでは、コードの中を見てみよう。何をいうにも、コードを見るのが理解の一番の手助けだ。たとえば CFArray の「実際」の定義を見てみよう。可変長の CFArray は、次のような形で定義されている。

CoreFoundation/Collections.subproj/CFArray.c
struct __CFArrayImmutable {
    CFRuntimeBase _base;
    CFIndex _count; /* number of objects */
};

CFRunttimeBase という型と、オブジェクトの数を現す _count でできている。実は、CoreFoundation のオブジェクトは CFRuntimeBase を必ず先頭に付けることになっているんだ。こいつの定義はこんな感じ。

CoreFoundation/Base.subproj/CFRuntime.h
typedef struct __CFRuntimeBase {
    void *_isa;
#if defined(__ppc__)
    uint16_t _rc;
    uint16_t _info;
    ...
#endif
} CFRuntimeBase;

いまのところ、_isa_rc_info の 3 つの変数が定義されている。_isa は、Cocoa に詳しい人ならおなじみの isa ポインタだ。Objective-C とのハイブリッドを実現するために用意されてる。_rc は retain count だ。参照回数を現していて、retain と release の操作のときに使われる。

そして _info フィールドが、現在の実装では Type ID の情報を持っているんだ。この _info フィールドから Type ID を取り出すところを見てみよう。__CFGenericTypeID() っていう関数だ。

CoreFoundation/Base.subproj/CFRuntime.c
CF_INLINE CFTypeID __CFGenericTypeID(const void *cf) {
    return __CFBitfieldGetValue(((const CFRuntimeBase *)cf)->_info, 15, 8);
}

__CFBitfieldGetValue() というのは、ビットフィールドの値を取り出すマクロで、ここでは 8 bit 目から 15 bit 目までを取り出している。ということで、_info のうち 8 bit を使っているんだ。ちなみに予想される _info のフィールドのうちわけはこんな感じ。


Defintion of _info field

Type ID と、system default allocator を使っているかどうかを示すビットがある。これは、ソースコードから推測した図なので、あまりあてにしないでくれ。

というわけで、各オブジェクトには 8 byte の CFRuntimeBase 型があって、そこに型情報が埋め込まれている、というこ話でした。

Type ID の割り当て

Type ID は固定の値が決まっているわけではなくて、ランタイムが走ったときに動的に割り当てられるんだ。各オブジェクトの初期化関数で、_CFRuntimeRegisterClass() っていう関数を呼び出している。

CoreFoundation/Base.subproj/CFRuntime.c
CFTypeID _CFRuntimeRegisterClass(const CFRuntimeClass * const cls) {
// version field must be 0
// className must be pure ASCII string, non-null
    __CFRuntimeClassTable[__CFRuntimeClassTableCount++] = 
                (CFRuntimeClass *)cls;
    return __CFRuntimeClassTableCount - 1;
}

ここの __CFRuntimeClassTable っていうグローバル領域にクラスの情報が入れられて、それに対応するインデックスが Type ID になるんだ。ちなみに、この配列の最大値は 256。Type ID が 8 bit だから当然だね。

この関数を呼べば、新しい CoreFoundation オブジェクトを登録できる、、、の、かも。実験してないからちょっとわからない。


Home | Link | Download | Back Number | Speciall Issue

mailto: mkino@xd5.so-net.ne.jp

HMDT