1

Control2 つの COM オブジェクトがあります (それらを と と呼びましょうJob)。コントロールは CoCreatable で、Job オブジェクトは によって作成されControl.NewJob()ます。

コントロールにはControl.Start(job)、指定されたジョブを現在のジョブにするメソッドがあります。他のジョブが設定されていない限り、現在のジョブのままです。

クライアントの場合、これらの特定のコントロールに対して次の動作が妥当に見えます。

  • ジョブの 1 つが存在する限り、コントロールは存在します
    (自明: ジョブは作成したコントロールへの強力な参照を保持します)。

  • クライアントが Control またはその CurrentJob への参照を持っている限り、どちらも破棄されません (「自明」: CurrentJob は強力な参照です)。

  • クライアントは、参照を解放する前に CurrentJob を「クリア」する必要はありません。

さて、ここに古典的な循環参照があります。それを解放する条件は、両方のオブジェクトが外部参照を持たないことです。

このシナリオは、ATL の InternalRelease 実装をいじることで解決できますが、これは非常に見苦しく、孤立しています

助言がありますか?既存のソリューション?

4

2 に答える 2

2

そのジョブの 1 つが存在する限り、コントロールは存在します。

いいえ、これがあなたが間違ったルールであることは間違いありません。IControl::CreateJob() ファクトリ関数を追加した唯一の理由は、CJob (インターフェイスではなく実装クラス) に CControl* 参照を与えるためです。これは所有権を意味し、特定の IJob は特定の Control インスタンスにのみ関連付けることができます。したがって、CControl は、所有する CJobs のコレクションを保持する必要があります。

今では簡単になります:

  • CControl インスタンスは通常どおり作成され、 AddRef() 呼び出しは1 回だけで、アプリだけがインスタンスを所有します。
  • IControl::CreateJob() 実装メソッドは、CJob(Control*) コンストラクターを使用して新しい CJob を作成し、それをコレクションに追加して、IJob インターフェイス ポインターを返します。AddRef() 呼び出しは1 つだけで、クライアント アプリだけがインスタンスを所有します。
  • CJob デストラクタは、プライベート CControl::RemoveJob() メソッドを呼び出して、コンストラクタから取得した CControl* が null でない場合はそれを使用して、コレクションから自身を削除します。
  • CControl デストラクタはそのコレクションを反復処理し、残りの CJob が保持する CControl* を null に戻します。それらは現在所有されていません。
  • IControl::Start(IJob*) 実装メソッドは、AddRef() を呼び出して、ジョブが存続していることを確認します。ジョブが完了または置換されたとき、または CControl オブジェクトが破棄されたときに解放します。エラー チェックが必要であることに注意してください。メソッドは、一致する CJob オブジェクトを見つけるためにコレクションを反復処理する必要があります。そのため、クライアントは、別の Control インスタンスによって作成されたジョブを開始できません。また、CJob* を CControl::currentJob プライベート変数として保持できます。
于 2014-10-23T15:21:17.037 に答える