|
HMDT - Special Issue / CoreFoundation の秘密 / String Services - 定数文字列の作り方 |
- String Servcies -
|
|
(sample)
|
CFStringRef hello = CFSTR("Hello, world.");
|
という感じで、C の文字列から CFString を作り出すんだ。ここで気をつけなきゃいけないのは、CFSTR() は新しい CFString のインスタンスを作り出すものではない、ということだ。だから、ここで得られた参照を release してはいけない。Apple の説明によると、「これは C で "hello world" ってプログラムの中に書いたようなものなんだ。ほら、こういうときは free しないでしょ?それと似たようなもので、CFSTR() で得られた文字列は release してはいけないんだ」
いささか無理があるような気もする説明だ。で、実際のところはどうなっているかというと、CFSTR() で作られた文字列はキャッシュされてるんだ。最初の 1 個は普通に作られて、キャッシュされる。次に同じものが要求されたときは、キャッシュされているインスタンスを返す。だから、次のソースコードの 2 つの関数は、メモリの使用量という観点からは、同じパフォーマンスのはずだ。
|
(sample)
|
void sample0() {
CFStringRef tmp;
tmp = CFSTR("Sample string");
func0(tmp);
func1(tmp);
}
void sample1() {
func0(CFSTR("Sample string"));
func1(CFSTR("Sample string"));
}
|
一度作られた定数文字列はキャッシュされるから、インスタンスが複数生成されるわけではないんだ。
CFSTR() に関する他の注意点は、こいつは仕様では 7 bit 文字しか受け付けないこと。一応、Mac OS Roman の文字は対応するように実装されているようだけど、将来どうなるかはわからんので使わない方が無難。とうぜん、是非は別にして、日本語も不可。
![]()
文字列をキャッシュしておくテーブルには CFMutableDictionary を使っている。constantStringTable っていう変数だ。
|
CoreFoundation/String.subproj/CFString.c
|
static CFMutableDictionaryRef constantStringTable = NULL; |
こいつのインスタンスは、CFSTR() で呼ばれる __CFStringMakeConstantString() が初めて呼ばれたときに作られる。ちなみに、容量の初期値は 2500。
![]()
では本題、実装を見ていこう。CFSTR() マクロの定義はこう。
|
CoreFoundation/String.subproj/CFString.h
|
CoreFoundation/Strign.subproj/CFString.h
#ifdef __CONSTANT_CFSTRINGS__
#define CFSTR(cStr) ((CFStringRef) __builtin___CFStringMakeConstantString ("" cStr ""))
#else
#define CFSTR(cStr) __CFStringMakeConstantString("" cStr "")
#endif
|
というわけで、__CFStringMakeConstantString() が呼び出される。これは定数文字列を作るための関数。この中での処理の流れは次のような感じだ。
これを頭において、実際のコードを抜き出してみたよ。
|
CoreFoundation/String.subproj/CFString.c
|
CFStringRef __CFStringMakeConstantString(const char *cStr) {
CFStringRef result;
/* キャッシュテーブルがあるかどうかチェック */
if (constantStringTable == NULL) {
/* キャッシュテーブルが無かった場合 */
CFDictionaryKeyCallBacks constantStringCallBacks =
{0, NULL, NULL, __cStrCopyDescription, __cStrEqual, __cStrHash};
/* キャッシュテーブルをつくる */
constantStringTable = CFDictionaryCreateMutable(
NULL, 0, &constantStringCallBacks, &kCFTypeDictionaryValueCallBacks);
_CFDictionarySetCapacity(constantStringTable, 2500); // avoid lots of rehashing
...
}
__CFSpinLock(&_CFSTRLock);
/* キャッシュテーブルから文字列を取り出す */
if (result = (CFStringRef)CFDictionaryGetValue(constantStringTable, cStr)) {
/* 文字列があった場合 */
__CFSpinUnlock(&_CFSTRLock);
} else {
/* 文字列が無かった場合 */
__CFSpinUnlock(&_CFSTRLock);
/* ASCII かどうかチェック */
...
if (!isASCII) {
/* ASCII じゃなかったときの処理 */
}
/* 文字列をつくる */
result = CFStringCreateWithCString(
constantStringAllocatorForDebugging, cStr, kCFStringEncodingMacRoman);
/* エラーチェックなど */
...
{
...
/* キャッシュテーブルに文字列を入れる */
CFIndex count;
__CFSpinLock(&_CFSTRLock);
count = CFDictionaryGetCount(constantStringTable);
CFDictionaryAddValue(constantStringTable, key, result);
if (CFDictionaryGetCount(constantStringTable) == count) {
// add did nothing, someone already put it there
result = (CFStringRef)CFDictionaryGetValue(
constantStringTable, key);
}
__CFSpinUnlock(&_CFSTRLock);
...
}
}
return result;
}
|
大きな流れはこんな感じ。キャッシュテーブルの処理がメインだ。ASCII じゃないときのエラー処理もあるけど、ここでは省いた。
実際に文字列を作ることになるのは、CFStringCreateWithCString() 関数だ。これについては、次の項で見るよ。
(Nov. 19, 2002)
|
Home | Link | Download | Back Number | Speciall Issue
|