コレクションの作成とコピー
コレクションオブジェクトの作成とコピーには、他のほとんどの Core Foundation 型よりも多くのオプションがあります。コレクションオブジェクトは不変または可変であり、後者であれば固定サイズでも可変サイズでもよい (不変オブジェクトはもちろん、常に固定サイズです)。これらの変異体にはそれぞれ独自の可能性と限界があります。
コレクションが作成されると、不変のコレクション内の値 (辞書の場合、キーなど) は変更できないため、オブジェクトの作成時にこれらの値を指定しなければなりません。これらの初期化値の受け入れ可能な形式は、(コレクションが 1 つの値のみを保持しない限り) C の配列です。入力パラメータは、この C の配列のアドレスを指定しなければなりません。リスト 1 は、CFArray オブジェクトの作成方法を示しています。
リスト 1 : 不変の CFArray オブジェクトの作成
CFStringRef strs[3]; CFArrayRef anArray; strs[0] = CFSTR("String One"); strs[1] = CFSTR("String Two"); strs[2] = CFSTR("String Three"); anArray = CFArrayCreate(NULL, (void *)strs, 3, &kCFTypeArrayCallBacks); CFShow(anArray); CFRelease(anArray);
CFArrayCreate 呼び出しの最後のパラメータである kCFTypeArrayCallBacks 定数のアドレスに注目してください。この定数は、CFArray 型の定義済みの 呼び出し関数構造体 を識別します。配列、辞書、セット、バッグなどのコレクションオブジェクトを作成およびコピーする関数では、呼び出し関数構造体を指定する必要があります。これらの構造体には、コレクションの値 (およびキー) の保持、評価、および記述方法を制御する呼び出し関数へのポインタが含まれています。上記の各コレクション型は、コレクションの値が Core Foundation オブジェクトの場合に使用できる 1 つ以上の定義済みの呼び出し関数構造体を定義します。あらかじめ定義されたコレクション呼び出し関数構造体はすべて、CFRetain を保持 (retain) 呼び出し関数として使用し、CFRelease を解放呼び出し関数として使用して、コレクションに追加されたオブジェクトは保持され、コレクションから削除されたオブジェクトが解放されるようにします。
不変の辞書オブジェクトを作成する CFDictionaryCreate 関数は、関連するコレクション関数とは多少異なります。1 つ以上の値だけでなく、これらの値に一致するキーのセットを指定する必要があります。これらの値とキーのリストを指定する一般的な方法は、2 つの C の配列です。リスト 2 に、簡単な例を示します。
リスト 2 : 不変の CFDictionary オブジェクトの作成
CFStringRef keys[3]; CFStringRef values[3]; CFDictionaryRef aDict; keys[0] = CFSTR("Key1"); keys[1] = CFSTR("Key2"); keys[2] = CFSTR("Key3"); values[0] = CFSTR("Value1"); values[1] = CFSTR("Value2"); values[2] = CFSTR("Value3"); aDict = CFDictionaryCreate(NULL, (void **)keys, (void **)values, 3, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFShow(aDict); CFRelease(aDict);
ある配列内のキーは、他の配列内の値と位置的に一致します。したがって、上記の例では、keys C 配列の 3 番目の要素は、values 配列内の 3 番目の要素のキーです。辞書オブジェクトを作成するには、キーと値の両方に対して初期化された呼び出し関数構造体を指定しなければなりません。
変更可能なコレクションオブジェクトを作成するには、与えられた型に適した CreateMutable 関数を呼び出して下さい。この呼び出しは、値を追加すべき空の (つまり値のない) コレクションを作成します。
CFMutableDictionaryRef myDictionary = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFDictionaryAddValue(myDictionary, CFSTR(“Age”), CFSTR(“35”));
これらの関数で呼び出し関数を指定する必要がない点を除いて、変更可能なコピーを作成するための同様のインターフェイスが存在します。この省略の理由は、可変コピーと不変コピーの両方に適用可能です。元のオブジェクトによって使用される呼び出し関数が、そのコピーを使用してその値を保持し、解放し、比較し、および記述するためです。
/* props is an existing dictionary */ CFMutableArrayRef urls = CFArrayCreateMutableCopy(NULL, 0, (CFArrayRef) CFDictionaryGetValue(props, kCFURLFileDirectoryContents));
変更可能なコレクションオブジェクトを作成またはコピーする関数では、2 番目のパラメータはコレクションの 容量、またはコレクションが安全に保存できる値の最大数を指定する整数です。容量が 0 より大きい可変コレクションは、固定サイズと呼ばれます。上記の例のように、このパラメータが 0 の場合、呼び出しは可変サイズのコレクションを要求します。可変サイズのコレクションには、任意の数の値を含めることができ、アドレス空間と使用可能なメモリによってのみ制限されます。
カスタムコレクション呼び出し関数の定義
これまでにリストされたすべてのコードの抜粋は、パラメータで指定された定義済みのコレクションの呼び出し関数構造体の 1 つを使用した作成関数を示しています。ただし、コレクションオブジェクトに独自のカスタムの呼び出し関数構造体を定義して使用することはできます。これをしたいときに少なくとも 2 つの場合があります。1 つは、コレクションに格納されている値が、独自の保持、解放、等価性テスト、または記述的な動作を必要とするカスタムデータ構造体である場合です。別の場合としては、定義済みの呼び出し関数構造体を使用したいが、その動作の一面を変更する必要がある場合です。
リスト 3 は、後者の場合の例を示しています。定義済みの kCFTypeDictionaryValueCallBacks 構造体に基づいてカスタムの CFDictionaryValueCallBacks 構造体を定義します。しかし、それは retain と release の関数ポインタを NULL に設定します。このコレクションに追加またはそれから削除された値は保持されず、解放もされません。これは、一部のデータ型に対して望ましい動作かも知れません。
リスト 3 : 変更された定義済みの呼び出し関数で CFDictionary オブジェクトの作成
CFMutableDictionaryRef bundlesByURL; {CFDictionaryValueCallBacks nonRetainingDictionaryValueCallbacks = kCFTypeDictionaryValueCallBacks; nonRetainingDictionaryValueCallbacks.retain = NULL; nonRetainingDictionaryValueCallbacks.release = NULL; bundlesByURL = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &nonRetainingDictionaryValueCallbacks); /* assume url and bundle come from somewhere */ CFDictionarySetValue(bundlesByURL, url, bundle);
リスト 4 の拡張したコードの例は、キーが整数であり、その値がプログラム定義の構造体である、変更可能な CFDictionary オブジェクトの作成を示しています。値とキーの両方にカスタムの呼び出し関数が定義されています。
リスト 4 : カスタム値とキーの呼び出し関数を使用した CFDictionary オブジェクトの作成
typedef struct { int someInt; float someFloat; } MyStructType; const void *myStructRetain(CFAllocatorRef allocator, const void *ptr) { MyStructType *newPtr = (MyStructType *)CFAllocatorAllocate(allocator, sizeof(MyStructType), 0); newPtr->someInt = ((MyStructType *)ptr)->someInt; newPtr->someFloat = ((MyStructType *)ptr)->someFloat; return newPtr; } void myStructRelease(CFAllocatorRef allocator, const void *ptr) { CFAllocatorDeallocate(allocator, (MyStructType *)ptr); } Boolean myStructEqual(const void *ptr1, const void *ptr2) { MyStructType *p1 = (MyStructType *)ptr1; MyStructType *p2 = (MyStructType *)ptr2; return (p1->someInt == p2->someInt) && (p1->someFloat == p2->someFloat); } CFStringRef myStructCopyDescription(const void *ptr) { MyStructType *p = (MyStructType *)ptr; return CFStringCreateWithFormat(NULL, NULL, CFSTR("[%d, %f]"), p->someInt, p->someFloat); } Boolean intEqual(const void *ptr1, const void *ptr2) { return (int)ptr1 == (int)ptr2; } CFHashCode intHash(const void *ptr) { return (CFHashCode)((int)ptr); } CFStringRef intCopyDescription(const void *ptr) { return CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), (int)ptr); } void customCallBackDictionaryExample(void) { CFDictionaryKeyCallBacks intKeyCallBacks = {0, NULL, NULL, intCopyDescription,intEqual, intHash}; CFDictionaryValueCallBacks myStructValueCallBacks = {0, myStructRetain, myStructRelease, myStructCopyDescription, myStructEqual}; MyStructType localStruct; CFMutableDictionaryRef dict; CFTypeRef value; /* Create a mutable dictionary with int keys and custom struct values ** whose ownership is transferred to and from the dictionary. */ dict = CFDictionaryCreateMutable(NULL, 0, &intKeyCallBacks, &myStructValueCallBacks); /* Put some stuff in the dictionary ** Because the values are copied by our retain function, we just ** set some local struct and pass that in as the value. */ localStruct.someInt = 1000; localStruct.someFloat = -3.14; CFDictionarySetValue(dict, (void *)42, &localStruct); localStruct.someInt = -1000; localStruct.someFloat = -3.14; CFDictionarySetValue(dict, (void *)43, &localStruct); /* Because the same key is used, this next call ends up replacing the earlier value (which is freed). */ localStruct.someInt = 44; localStruct.someFloat = -3.14; CFDictionarySetValue(dict, (void *)42, &localStruct); show(CFSTR("Dictionary: %@"), dict); value = CFDictionaryGetValue(dict, (void *)43); if (value) { MyStructType result = *(MyStructType *)value; CFStringRef description = myStructCopyDescription(&result); show(CFSTR("Value for key 43: %@"), description); CFRelease(description); } CFRelease(dict); }
CFArray、CFDictionary、CFSet、および CFBag のコレクション型は、呼び出し関数のために以下の構造体型を宣言します。
CFArrayCallBacks
CFDictionaryKeyCallBacks
CFDictionaryValueCallBacks
CFSetCallBacks
CFBagCallBacks
これら構造体の関数ポインタメンバは、許容可能な値、指示された関数の予想される動作、および警告に似ています。表 1 に、これらの呼び出し関数の一般的な特性の一部を示します。詳細については、呼び出し関数構造体型のリファレンス文書を参照してください。
表 1
関数ポインタ変数 | コレクション型 | 呼び出し関数の説明 |
---|---|---|
retain | すべて | コレクションに追加されるときに値を retain するために呼び出されます。参照カウントの性質は、データの型とコレクションの目的によって異なります。たとえば、参照カウントを増分できます。この関数はコレクションに格納する値を返し、通常は渡される値ですが、値を格納する必要がある場合は別の値にできます。関数ポインタは NULL でもかまいません。 |
release | すべて | コレクションから値を削除するときに呼び出されます。これは、例えば、参照カウントを減分するか、値に割り当てられたメモリを解放するなどして、retain 呼び出し関数の効果を元に戻します。関数ポインタは NULL でもかまいません。 |
equal | すべて | 2 つの値を比較する呼び出し関数。 一部の操作でコレクションの値の比較が必要な場合に呼び出されます。コレクション値の場合、関数ポインタは NULL にできます。コレクションキーの場合 NULL にできます。この場合、ポインタの等価性が比較の基礎として使用されます。 |
copyDescription | すべて | コレクション内の各値の説明 (CFString オブジェクトとして) を作成して返す呼び出し関数。この呼び出し関数は、CFCopyDescription 関数と CFShow 関数によって呼び出されます。関数ポインタは NULL にできます。その場合、コレクションは単純な記述で構成されます。 |
hash | CFDictionary keys, CFSet, CFBag | コレクション内の値にアクセスし、追加し、または削除するために使用されるキーのハッシュコードを計算するために呼び出される呼び出し関数。NULL が割り当てられている場合、デフォルトの実装では、ポインタアドレスをハッシュコードとして使用します。equal 関数と hash 関数の呼び出し関数関係の詳細については、辞書 を参照してください。 |
前の章 次の章