home link download back number special issue

HMDT - Special Issue / CoreFoundation の秘密 / Collection Services - CFArray の実装


- Collection Services -
CFArray の実装

Core Foundation は、配列を取り扱うためのコレクションオブジェクト CFArray と CFMutableArray を提供しているぜ。CFArray が固定長で、CFMutableArray が可変長ね。いちおう言っておくけど、配列っていっても C の配列とは違うからな。

CFArray の実体

まずは CFArray の定義を見てみよう。ずばり、こうだ。

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

最初に CFRuntimeBase があって、次に配列の中にあるオブジェクトの数がある。

CFMutableArray の方はこんな感じ。

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

CFRuntimeBase と、オブジェクトの数があって、格納できるオブジェクトの最大数がある。

あれ?じゃ、実際に入るオブジェクトはどこに?実は、この構造体は配列のヘッダみたいなもので、こいつらの後ろにメモリが確保されてその中に入る。オブジェクトは次の __CFArrayBucket っていう構造体に入れられるんだ。

CoreFoundation/Collections.subproj/CFArray.c
struct __CFArrayBucket {
    const void *_item;
};

まぁ、ただのポインタだわな。というわけで、CFArray と CFMutableArray の構造を図に描くと、こうなる。

メモリの固まりにオブジェクトが入っているんだ。リンクリストじゃないんだよ。

配列を作る

次は配列を作るところを見てみよう。配列を作る API は次の 4 つ。固定長か可変長か、オブジェクトをコピーするしないか、で分かれる。

CoreFoundation/Collections.subproj/CFArray.h
CF_EXPORT
CFArrayRef CFArrayCreate(
                CFAllocatorRef allocator, 
                const void **values, 
                CFIndex numValues, 
                const CFArrayCallBacks *callBacks);
CF_EXPORT
CFArrayRef CFArrayCreateCopy(
                CFAllocatorRef allocator, 
                CFArrayRef theArray);
CF_EXPORT
CFMutableArrayRef CFArrayCreateMutable(
                CFAllocatorRef allocator, 
                CFIndex capacity, 
                const CFArrayCallBacks *callBacks);
CF_EXPORT
CFMutableArrayRef CFArrayCreateMutableCopy(
                CFAllocatorRef allocator, 
                CFIndex capacity, 
                CFArrayRef theArray);

これらの実装がどうなっているかというと、4 つとも __CFArrayInit() っていう関数を呼び出している。この中で CFArray を作ったり、メモリを確保したりしているんだ。こいつを見てみよう。

CoreFoundation/Collections.subproj/CFArray.c
static CFArrayRef __CFArrayInit(
                CFAllocatorRef allocator, 
                UInt32 flags, 
                CFIndex capacity, 
                const CFArrayCallBacks *callBacks) 
{
    struct __CFArrayImmutable *memory;
    UInt32 size;
    ...

    size = __CFArrayGetSizeOfType(flags) - sizeof(CFRuntimeBase);
    switch (__CFBitfieldGetValue(flags, 1, 0)) {
    case __kCFArrayImmutable:
    case __kCFArrayFixedMutable:
        /* サイズを決定 */
        /* 容量の大きさ×__CFArrayBucket の大きさ */
        size += capacity * sizeof(struct __CFArrayBucket);
        break;
    case __kCFArrayMutableDeque:
    case __kCFArrayMutableStore:
        break;
    }
    /* サイズの大きさだけインスタンスを確保 */
    memory = (struct __CFArrayImmutable *)_CFRuntimeCreateInstance(
                                allocator, __kCFArrayTypeID, size, NULL);
    if (NULL == memory) {
        return NULL;
    }
    ...

    return (CFArrayRef)memory;
}

ということで、指定した大きさの分だけバケツを確保してできあがり、というわけだ。

配列の実装をメモリの固まりで行っている(リンクリストじゃない)ことから、オブジェクトをインデックスを指定して取得するのは速いけど、追加したり削除したりという操作は遅いことが予想できるよね。次の項で、そこを見てみよう。

(Jan. 24, 2003)


Home | Link | Download | Back Number | Speciall Issue

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

HMDT