参照カウントの管理に関する規則
参照カウントを使用してオブジェクトの有効期間を管理すると、複数のクライアントがオブジェクトの有効期間を管理する際に相互に調整することなく、単一のオブジェクトへのアクセスを取得および解放できるようになります。 クライアント オブジェクトが特定の使用規則に準拠している限り、オブジェクトは事実上、この管理を提供します。 これらの規則では、オブジェクト間の参照を管理する方法を指定します。 (COM はオブジェクトの内部実装を指定しませんが、これらのルールはオブジェクト内のポリシーの適切な開始点です。)
概念的には、インターフェイス ポインターは、インターフェイス ポインターを保持するすべての内部計算状態を含むポインター変数内に存在すると考えることができます。 これには、メモリ位置、内部プロセッサ レジスタ内の変数、およびプログラマーが生成した変数とコンパイラが生成した変数の両方が含まれます。 ポインター変数への代入または初期化には、既存のポインターの新しいコピーの作成が含まれます。 何らかの変数 (代入/初期化で使用される値) にポインターのコピーが 1 つある場合、2 つになりました。 ポインター変数への代入は、変数自体の破棄と同様に、変数内に存在するポインター コピーを破棄します。 (つまり、スタック フレームなど、変数が見つかったスコープは破棄されます)。
COM クライアントの観点からは、参照カウントは常にインターフェイスごとに行われます。 クライアントは、オブジェクトがすべてのインターフェイスに同じカウンターを使用することを想定しないでください。
既定のケースでは、インターフェイス ポインターの新しいコピーごとに AddRef を呼び出す必要があり、次の規則で許可されている場合を除き、インターフェイス ポインターを破棄するたびに Release を呼び出す必要があります。
- 関数への入出力パラメーター。 呼び出し元は、out 値をその上に格納するときに実装コードでリリース (Release の呼び出しを伴う) ため、パラメーターに対して AddRef を呼び出す必要があります。
- グローバル変数のフェッチ。 グローバル変数内のポインターの既存のコピーからインターフェイス ポインターのローカル コピーを作成する場合は、ローカル コピーが有効な間に別の関数がグローバル変数のコピーを破棄する可能性があるため、ローカル コピーで AddRef を呼び出す必要があります。
- 「薄い空気」から合成された新しいポインター。他のソースから取得するのではなく、特別な内部知識を使用してインターフェイス ポインターを合成する関数は、新しく合成されたポインターで最初に AddRef を呼び出す必要があります。 このようなルーチンの重要な例としては、インスタンス作成ルーチン、QueryInterface の実装などがあります。
- 内部に格納されているポインターのコピーを取得しています。 関数が呼び出されたオブジェクトによって内部的に格納されているポインターのコピーを取得する場合、そのオブジェクトのコードは、関数が戻る前にポインターに対して AddRef を呼び出す必要があります。 ポインターが取得されると、元のオブジェクトには、その有効期間が内部に保存されているポインターのコピーの有効期間とどのように関係するかを判断する他の方法はありません。
既定のケースの唯一の例外として、管理コードがオブジェクト上の同じインターフェイスへのポインターの 2 つ以上のコピーの有効期間の関係を認識し、その参照カウントをゼロにすることでオブジェクトが破棄されないようにする必要があります。 一般に、次の 2 つのケースがあります。
- ポインターのコピーが 1 個既に存在し、2 番目のコピーが続けて作成されて、最初のコピーがまだ存在する間に破棄された場合、2 番目のコピーに対する AddRef と リリース の呼び出しを省略できます。
- ポインターのコピーが 1 個存在し、2 番目のコピーが作成されてから、2 番目より先に最初のコピーが破棄されると、2 番目のコピーの AddRef と最初のコピーの リリース の呼び出しを省略できます。
これらの状況の具体的な例を次に示します。最初の 2 つは特に一般的です。
- 関数へのパラメーター内。 関数にパラメーターとして渡されるインターフェイス ポインターのコピーの有効期間は、値の初期化に使用されるポインターのコピーに入れ子になるため、パラメーターに別の参照カウントを設ける必要はありません。
- 戻り値を含む関数からパラメーターを出力します。 out パラメーターを設定するには、関数にインターフェイス ポインターの安定したコピーが必要です。 戻ったとき、呼び出し元はポインターを解放する責任があります。 したがって、out パラメーターには個別の参照カウントは必要ありません。
- ローカル変数。 メソッドの実装では、スタック フレームに割り当てられた各ポインター変数の有効期間を制御できます。これを使用して、冗長な AddRef/Release ペアを省略する方法を決定できます。
- バックポインター。 一部のデータ構造には 2 つのオブジェクトが含まれています。各オブジェクトは他方へのポインターを持ちます。 最初のオブジェクトの有効期間に 2 番目のオブジェクトの有効期間が含まれることがわかっている場合、最初のオブジェクトへの 2 番目のオブジェクトのポインターの参照カウントを持つ必要はありません。 多くの場合、このサイクルを回避することは、適切な割り当て解除動作メイン維持する上で重要です。 ただし、オペレーティング システムのリモート処理を処理する部分ではこの関係を知る方法がないため、カウントされないポインターは細心の注意を払って使用する必要があります。 したがって、ほとんどの場合、バックポインターに最初のポインターの 2 番目の「フレンド」オブジェクトを参照させる (これにより、循環性を回避する) ことが望ましい解決策となります。 たとえば、COM の接続可能なオブジェクト アーキテクチャでは、このアプローチが使用されます。
参照カウントされたオブジェクトを実装または使用する場合は、関数の処理中にオブジェクトの安定性を保証する人工参照カウントを適用すると便利な場合があります。 インターフェイスのメソッドを実装する際に、オブジェクトへの参照カウントをデクリメントする可能性のある関数を呼び出すと、オブジェクトが早期に解放され、実装が失敗する可能性があります。 これを回避する堅牢な方法は、メソッド実装の先頭に AddRef の呼び出しを挿入し、メソッドが戻る直前に Release の呼び出しとペアリングすることです。
状況によっては、AddRef と Release の戻り値が不安定になり、依存しないようにする必要があります。デバッグまたは診断の目的でのみ使用してください。
関連トピック