|
HMDT - Special Issue / CoreFoundation の秘密 / Base Services - 参照回数によるメモリ管理 |
- Base Services -
|
|
CoreFoundation/Base.subproj/CFRuntime.h
|
typedef struct __CFRuntimeBase {
...
uint16_t _rc;
...
} CFRuntimeBase;
|
この _rc がが参照回数の実体ね。ここでは 16 bit で定義されていることに注意。
続いて、オブジェクトを確保するところ。ここで参照回数の初期値を設定する。
|
CoreFoundation/Base.subproj/CFRuntime.c
|
CFTypeRef _CFRuntimeCreateInstance(
CFAllocatorRef allocator,
CFTypeID typeID,
uint32_t extraBytes,
unsigned char *category)
{
CFRuntimeBase *memory;
...
/* メモリ確保 */
memory = CFAllocatorAllocate(allocator, size, 0);
if (NULL == memory) {
return NULL;
}
...
/* CFRuntimeBase のフィールド設定 */
memory->_isa = __CFISAForTypeID(typeID);
memory->_rc = 1;
memory->_info = 0;
__CFBitfieldSetValue(memory->_info, 15, 8, typeID);
...
return memory;
}
|
こんな感じだ。参照回数は 1 に設定されてるね。
続いて retain。参照回数を上げる操作だ。
|
CoreFoundation/Base.subproj/CFRuntime.c
|
CFTypeRef CFRetain(CFTypeRef cf) {
CFIndex highBits = 0, lowBits = 0;
...
/* 参照回数の取得 */
lowBits = ((CFRuntimeBase *)cf)->_rc;
if (0 == lowBits) { // Constant CFTypeRef
...
return cf;
}
/* 参照回数を上げる */
lowBits++;
/* 16 bit 目のチェック */
if ((lowBits & 0x07fff) == 0) {
/* 外部にあるはずの highBits を取得 */
// Roll over another bit to the external ref count
highBits = (CFIndex)CFDictionaryGetValue(
__CFRuntimeExternRefCountTable,
cf); // NULL return -> 0 ref count
/* highBits を上げる */
highBits++;
CFDictionarySetValue(
__CFRuntimeExternRefCountTable,
cf,
(void *)highBits);
/* lowBits を 0x8000 に設定 */
lowBits = 0x8000; // Bit 16 indicates external ref count
}
/* 参照回数をメモリに設定 */
((CFRuntimeBase *)cf)->_rc = lowBits;
...
return cf;
}
|
こんなわけで、CFRuntimeBase の _rc フィールドは 16 bit だけど、参照回数自体は 32 bit としてあつかっていることが分かるんだ。カラクリはどうなっているかというと、参照回数の 32 bit を _rc の lowBits (16 bit) と、外部にある highBIts (16 bit) に分ける。_lowBits を上げていって、0x8000 になったら外部に highBits を確保する。lowBits の 16 bit 目は highBits があるかどうかの判定に使われるんだ。だからビットフィールドの使われ方はこうなる。

Definition of reference count
だから実際に参照回数として使われるのは 31 bit だね。仮に 16 bit だとすると、参照回数の最大値は 65535。これだと足りなくなることもあるか。でも、各オブジェクトに 32 bit 持たせたりすると、無駄なメモリ領域ができることになってしまう。というわけで、上位 16 bit を外部に持たせているようだ。それともただ単に、初め 16 bit にしておいて、後で足りなくなっちゃったー、ということで拡張しただけかも。
![]()
上げたら下げなくてはいけない。というわけで、次は release するときのコード。
|
CoreFoundation/Base.subproj/CFRuntime.c
|
void CFRelease(CFTypeRef cf) {
CFIndex highBits = 0, lowBits = 0;
...
/* 参照回数の取得 */
lowBits = ((CFRuntimeBase *)cf)->_rc;
if (0 == lowBits) { // Constant CFTypeRef
...
return;
}
if (1 == lowBits) {
if (__kCFAllocatorTypeID_CONST == __CFGenericTypeID(cf)) {
/* オブジェクトを削除 */
__CFAllocatorDeallocate((void *)cf);
} else {
CFAllocatorRef allocator;
if (NULL != __CFRuntimeClassTable[__CFGenericTypeID(cf)]->finalize) {
/* 各オブジェクトの finalize() を呼び出す */
__CFRuntimeClassTable[__CFGenericTypeID(cf)]->finalize(cf);
}
...
CFAllocatorDeallocate(allocator, (void *)cf);
...
}
} else {
if (0x8000 == lowBits) {
/* highBits があるときの処理 */
// Time to remove a bit from the external ref count
highBits = (CFIndex)CFDictionaryGetValue(
__CFRuntimeExternRefCountTable,
cf); // NULL return -> 0 ref count
/* highBits を下げる */
highBits--;
if (0 == highBits) {
/* highBits を削除する */
CFDictionaryRemoveValue(
__CFRuntimeExternRefCountTable,
cf);
lowBits = 0x07fff;
} else {
CFDictionarySetValue(
__CFRuntimeExternRefCountTable,
cf,
(void *)highBits);
lowBits = 0x0ffff;
}
} else {
/* 参照回数を下げる */
lowBits--;
}
/* 参照回数をメモリに設定 */
((CFRuntimeBase *)cf)->_rc = lowBits;
}
}
|
こんな感じ。参照回数が 1 のときはオブジェクトを削除する。オブジェクトに finalize() があれば呼び出す。これはデストラクタみたいなものだね。そして CFAllocatorDeallocate() を呼び出してオブジェクトを削除する。
1 より大きいときは、参照回数を下げる。このとき、highBits を使っているかどうかで処理が変わるんだ。どっちにしても 1 つ減らして、処理はおしまい。
![]()
こんな感じで retain と release は実装されている。メモリ管理のポリシーはしっかりしているので、守っていればメモリの問題は起きないはずだ。ちなみに、Cocoa にはある autorelease は?どうも、ないみたい。autorelease 使えないので注意。
|
Home | Link | Download | Back Number | Speciall Issue
|