樹構造の作成と使用
CFTree の不透明型を使用すると、メモリ内に樹構造体を作成し、階層構造の情報を表現できます。このような構造体では、各樹のノードはに厳密に 1 つの親の樹 (親を持たないルートの樹を除く) があり、複数の子の樹を持つことができます。拡張構造体内の各 CFTree オブジェクトには、関連するコンテキストがあります。このコンテキストには、プログラムで定義されたデータと、そのデータ上で動作する呼び出し関数が含まれています。プログラムで定義されたデータは、構造体内の各ノードに関する情報を格納するためによく使用されます。
このタスクでは、CFTree オブジェクトを作成し、樹の構造体にフィットさせ、後でそれらを検索し、取得し、そして変更する方法について説明します。ほとんどの CFTree 関数の最初のパラメータは、親の CFTree オブジェクトへの参照です。操作それ自体には通常、その親の 1 つ以上の子が含まれます。言い換えれば、ほとんどの CFTree ルーチンは、与えられた親から階層を "下って" 行きますが、その親の子のみに影響します。
CFTree オブジェクトを操作する上で覚えておくべき重要な事実は、子に動作する関数は再帰的ではないということです。それらは直接子の樹にのみ影響します。任意の親ノードからすべてのサブツリーを横断したい場合は、コードが横断するロジックを提供しなければなりません。
CFTree オブジェクトの作成
CFTree オブジェクトを作成する前に、そのコンテキストを定義しなければなりません。つまり、CFTreeContext の構造体を宣言して初期化しなければなりません。この構造体の定義は以下のとおりです。
typedef struct { CFIndex version; void *info; const void *(*retain)(const void *info); void (*release)(const void *info); CFStringRef (*copyDescription)(const void *info); } CFTreeContext;
この構造体の info メンバーは、あなたが定義したデータを指し示しています (必要に応じて割り当てます)。コンテキスト構造体の他のメンバ (version メンバを除く) は、info ポインタを引数として受け取り、それを保持し、解放し、そして記述するなど、指し示す先のデータに関連する特定の操作を実行する関数を指し示します。
CFTreeContext 構造体を適切に初期化したら、構造体へのポインタを渡して CFTreeCreate 関数を呼び出します。リスト 1 に、このテクニックの例を示します。
リスト 1 : CFTree オブジェクトの作成
static CFTreeRef CreateMyTree(CFAllocatorRef allocator) { MyTreeInfo *info; CFTreeContext ctx; info = CFAllocatorAllocate(allocator, sizeof(MyTreeInfo), 0); info->address = 0; info->symbol = NULL; info->countCurrent = 0; info->countTotal = 0; ctx.version = 0; ctx.info = info; ctx.retain = AllocTreeInfo; ctx.release = FreeTreeInfo; ctx.copyDescription = NULL; return CFTreeCreate(allocator, &ctx); }
この例で示すように、CFTree オブジェクトのコンテキストのための呼び出し関数を定義したくない場合は、CFTreeContext 構造体の関数ポインタメンバーを NULL に初期化できます。
親に樹を追加する
どのような用途にも使用するには、樹構造体に CFTree オブジェクトを挿入しなければなりません。他の CFTree オブジェクトとの階層関係に配置されなければなりません。これを行うには、以下のいずれかの CFTree 関数の一つを使用して、オブジェクトを他のどれかの樹との関係で子または兄弟の樹にしなければなりません。
CFTreeAppendChild
CFTreePrependChild
CFTreeInsertSibling
リスト 2 は、その子たちの親のリストに追加 (Append) される子の樹を示しています。
リスト 2 : 親に子の CFTree を追加 (Add) する
/* assume anAddress and curTree already exist */ CFTreeRef child = FindTreeChildWithAddress(curTree, anAddress);if (NULL == child) { CFTreeContext ctx; child = CreateMyTree(CFGetAllocator(curTree)); CFTreeGetContext(child, &ctx); ((MyTreeInfo *)ctx.info)->address = anAddress; CFTreeAppendChild(curTree, child); CFRelease(child); }
このコード例は、CFTree プログラミングインタフェースの別の側面も示しています。場合によっては、すでに作成された CFTree オブジェクトに関連したプログラム定義データを変更する必要があります。これを行うには、そのオブジェクト上の CFTreeGetContext 関数を呼び出して樹のコンテキストを取得して下さい。この構造体を一度作成したら、info ポインタを使用してプログラムで定義したデータにアクセスできます。
子の樹の取得
CFTree 型には、子の樹を取得する関数がいくつかあります。兄弟の樹は順番に並んでいるため、通常、これらのメソッドのうち 2 つだけを使用して、1 つの親の子の樹である CFTreeGetFirstChild と CFTreeGetNextSibling を横断します。リスト 3 は、これを行う方法を示しています。
リスト 3 : 親樹の子の樹を横断する
static CFTreeRef FindTreeChildWithAddress(CFTreeRef tree, UInt32 addr) { CFTreeRef curChild = CFTreeGetFirstChild(tree); for (; curChild; curChild = CFTreeGetNextSibling(curChild)) { CFTreeContext ctx; CFTreeGetContext(tree, &ctx); if (((MyTreeInfo *)ctx.info)->address == addr) { return curChild; } } return NULL; }
すべての CFTree 関数が子の樹を作用したり返したりするわけではありません。たとえば、CFTreeGetParent 関数は、与えられた樹の親の樹を取得します。CFTreeFindRoot 関数は、現在の樹構造体のルートの CFTree オブジェクト、つまり親の樹を持たない構造体の樹を取得します。
CFTree 構造体でのその他の操作
CFTree 型には、他のコレクション関数と非常によく似た 2 つの関数があります。CFTreeApplyFunctionToChildren 関数は、親の CFTree オブジェクトの子にプログラム定義の適合 (applier) 関数を適用します。CFTreeSortChildren 関数は、親の CFTree オブジェクトの子を、定義できるコンパレータ関数を使用してソート (並べ替え) します (または、CFComparatorFunction 型に準拠している限り使用します)。
- CFTreeApplyFunctionToChildren に関連する使用法の詳細については、コレクションにプログラム定義関数を適用 を参照してください。
- CFArraySortValues 関数の使用については、変更可能なコレクションの操作 を参照してください。この情報の多くは、CFTreeSortChildren 関数に適用されます。