|
HMDT - Special Issue / CoreFoundation の秘密 / Base Services - 型の動的判別 |
- Base Services -
|
|
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 は固定の値が決まっているわけではなくて、ランタイムが走ったときに動的に割り当てられるんだ。各オブジェクトの初期化関数で、_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
|