4

インターフェイスに小さな問題があります。ここに擬似コードがあります:

type
  Interface1 = interface
  end;

  Interface2 = interface
  end;

  TParentClass = class(TInterfacedObject, Interface1)
  private
    fChild : Interface2;
  public
    procedure AddChild(aChild : Interface2);
  end;

  TChildClass = class(TInterfacedObject, Interface2)
  private
    fParent : Interface2;
  public
    constructor Create(aPArent : Interface1);
  end;

誰でも欠陥を見ることができますか?子がその親への参照を持つ必要がありますが、この状況では参照カウントが機能しません。ParentClass インスタンスを作成して子を追加すると、親クラスは解放されません。理由がわかります。どうすればそれを回避できますか?

4

4 に答える 4

10

参照カウント参照には2つのセマンティクスがあります。所有権の共有として機能するだけでなく、オブジェクトグラフをナビゲートする手段としても機能します。

通常、参照のグラフのサイクル内のすべてのリンクで、これらのセマンティクスの両方は必要ありません。おそらく、親だけが子供を所有し、その逆はありませんか?その場合は、次のようにポインタとして保存することで、子に親の弱いリンクを参照させることができます。

TChildClass = class(TInterfacedObject, Interface2)
private
  fParent : Pointer;
  function GetParent: Interface1;
public
  constructor Create(aPArent : Interface1);
  property Parent: Interface1 read GetParent;
end;

function TChildClass.GetParent: Interface1;
begin
  Result := Interface1(fParent);
end;

constructor TChildClass.Create(AParent: Interface1);
begin
  fParent := Pointer(AParent);
end;

これは、インスタンスのツリーのルートがどこかで存続することが保証されている場合、つまり、ツリーのブランチへの参照だけに依存せず、ツリー全体をナビゲートできる場合に安全です。

于 2008-10-05T10:23:13.793 に答える
3

もちろん、参照カウントはこの状況でも機能します。問題を解決することはできません。

これが参照カウントの最大の問題です。循環参照がある場合は、明示的に「壊す」必要があります(たとえば、1つのインターフェイス参照を「nil」に設定します)。これが、参照カウントが実際にはガベージコレクションの代わりにならない理由でもあります。ガベージコレクターは、サイクルが存在する可能性があることを認識しており、「外部」から参照されていない場合にそのような循環構造を解放できます。

于 2008-10-05T10:14:40.067 に答える
1

正しい参照を明示的にリンク解除するメソッドを作成する必要があります。この場合、自動参照カウントを適切に機能させる方法はありません。

于 2008-10-05T09:11:12.537 に答える
0

最初の例で関数ポインターを使用すると、循環参照の問題は存在しません。.NET はデリゲートを使用し、VB6 はイベントを使用します。これらすべてには、ポイントされているオブジェクトの参照カウントもインクリメントしないという利点があります。

于 2008-10-06T19:51:26.097 に答える