在数组中保存时释放接口对象的问题

时间:2012-04-02 23:52:45

标签: arrays delphi interface automatic-ref-counting delphi-xe2

为了提供尽可能多的信息,这里是我正在做的事情的一个非常基本的例子

type
  IMyInterface = interface
  [THE_GUID_HERE]
    // some methods
  end;

  TMyInterfaceArray = Array of IMyInterface;

  TMyInterfacedObject = class(TInterfacedObject, IMyInterface)
    // implementation of the Interface etc. here
  end;

  TContainingObject = class
  private
    FIObjectArray: TMyInterfaceArray;
  public
    constructor Create;
    destructor Destroy; override;
    procedure NewInstanceOfInterfacedObject;
  end;

  implementation

  constructor TContainingObject.Create;
  begin
    inherited;
    // Just to illustrate that an Instance is being created...
    NewInstanceOfInterfacedObject;
  end;

  destructor TContainingObject.Destroy;
  var
    I: Integer;
  begin
    for I := Low(FIObjectArray) to High(FIObjectArray) do
      FIObjectArray[I] := nil;
    SetLength(FIObjectArray, 0); // Array collapsed

    inherited;
  end;

  procedure TContainingObject.NewInstanceOfInterfacedObject;
  var
    LIndex: Integer;
  begin
    LIndex := Length(FIObjectArray);
    SetLength(FIObjectArray, LIndex + 1);
    FIObjectArray[LIndex] := TMyInterfacedObject.Create;
  end;

好的,因此创建了TContainingObject的实例,然后创建了TMyInterfacedObject的实例,存储在IMyInterface的数组中。

调用TContainingObject的{​​{1}}时,它只是引用并折叠数组。

我遇到的问题是,在任何地方都没有其他引用,destructor的{​​{1}}永远不会被调用,因此内存泄漏。

我做错了什么,或者Delphi的引用计数系统无法应对接口类型数组中接口对象的简单概念?

感谢您的任何建议!

更多信息

TMyInterfacedObject提供了一个Array属性来访问Array中包含的destructor个别实例。

在我的实际代码中,多个接口类型之间存在循环引用。 我们建议TContainingObject包含函数IMyInterfaceIMyInterface包含GetSomething: IAnotherInterface(循环引用)。 这会导致我的问题吗?如果是这样,循环引用是绝对必需,那么解决方案的目的是什么呢?

2 个答案:

答案 0 :(得分:5)

如果IMyInterface的实施包含IAnotherInterface成员,并且IAnotherInterface的实施包含IMyInterface成员,并且他们互相引用,那么他们的引用计数除非你清除其中一个引用,否则永远不会降到0,这可能意味着在接口中添加方法来执行此操作,例如:

type
  IAnotherInterface = interface;

  IMyInterface = interface
  ['{guid}']
    function GetAnotherInterface: IAnotherInterface;
    procedure SetAnotherInterface(Value: IAnotherInterface);
    property AnotherInterface: IAnotherInterface read GetAnotherInterface write SetAnotherInterface;
  end;

  IAnotherInterface = interface
  ['{guid}']
    function GetMyInterface: IMyInterface;
    procedure SetMyInterface(Value: IMyInterface);
    property MyInterface: IMyInterface read GetMyInterface write SetMyInterface;
  end;

type
  TMyInterface = class(TInterfacedObject, IMyInterface)
  private
    FAnotherInterface: IAnotherInterface;
  public
    function GetAnotherInterface: IAnotherInterface;
    procedure SetAnotherInterface(Value: IAnotherInterface);
  end;

  TAnotherInterface = class(TInterfacedObject, IAnotherInterface)
  private
    FMyInterface: IMyInterface;
  public
    function GetMyInterface: IMyInterface;
    procedure SetMyInterface(Value: IMyInterface);
  end;

  function TMyInterface.GetAnotherInterface;
  begin
    Result := FAnotherInterface;
  end;

  procedure TMyInterface.SetAnotherInterface(Value: IAnotherInterface);
  begin
    if FAnotherInterface <> Value then
    begin
      if FAnotherInterface <> nil then FAnotherInterface.SetMyInterface(nil);
      FAnotherInterface := Value;
      if FAnotherInterface <> nil then FAnotherInterface.SetMyInterface(Self);
    end;
  end;

  function TAnotherInterface.GetMyInterface: IMyInterface;
  begin
    Result := FMyInterface;
  end;

  procedure TAnotherInterface.SetMyInterface(Value: IMyInterface);
  begin
    if FMyInterface <> Value then
    begin
      if FMyInterface <> nil then FMyInterface.SetAnotherInterface(nil);
      FMyInterface := Value;
      if FMyInterface <> nil then FMyInterface.SetAnotherInterface(Self);
    end;
  end;

现在,当您没有明确地释放其中一个引用时,请查看引用计数:

var
  I: IMyInterface;
  J: IAnotherInterface;
begin
  I := TMyInterface.Create; // I.RefCnt becomes 1
  J := TAnotherInterface.Create; // J.RefCnt becomes 1
  I.AnotherInterface := J; // I.RefCnt becomes 2, J.RefCnt becomes 2
  ...
  {
  // implicit when scope is cleared:
  I := nil; // I.RefCnt becomes 1, I is NOT freed
  J := nil; // J.RefCnt becomes 1, J is NOT freed
  }
end;

现在为其中一个引用添加一个显式版本:

var
  I: IMyInterface;
  J: IAnotherInterface;
begin
  I := TMyInterface.Create; // I.RefCnt becomes 1
  J := TAnotherInterface.Create; // J.RefCnt becomes 1
  I.AnotherInterface := J; // I.RefCnt becomes 2, J.RefCnt becomes 2
  ...
  I.AnotherInterface := nil; // I.RefCnt becomes 1, J.RefCnt becomes 1
  {
  // implicit when scope is cleared:
  I := nil; // I.RefCnt becomes 0, I is freed
  J := nil; // J.RefCnt becomes 0, J is freed
  }
end;

var
  I: IMyInterface;
  J: IAnotherInterface;
begin
  I := TMyInterface.Create; // I.RefCnt becomes 1
  J := TAnotherInterface.Create; // J.RefCnt becomes 1
  I.AnotherInterface := J; // I.RefCnt becomes 2, J.RefCnt becomes 2
  J := nil; // I.RefCnt still 2, J.RefCnt becomes 1, J is NOT freed yet
  ...
  I.AnotherInterface := nil; // I.RefCnt becomes 1, J.RefCnt becomes 0, J is freed
  {
  // implicit when scope is cleared:
  I := nil; // I.RefCnt becomes 0, I is freed
  }
end;

答案 1 :(得分:0)

当我在Delphi XE2中运行你的代码示例时(修复了一个小错误 - 请参阅编辑),它运行正常,并按预期调用TMyInterfacedObject析构函数。