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

LaK*_*ven 1 arrays delphi interface delphi-xe2 automatic-ref-counting

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

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;
Run Code Online (Sandbox Code Playgroud)

好的,所以TContainingObject创建了一个实例,然后创建一个TMyInterfacedObject存储在Array of中的实例IMyInterface.

TContainingObjectdestructor被调用,它零的参考和崩溃的阵列.

我的问题是,与任何地方的任何其他引用,TMyInterfacedObjectdestructor永远不会被调用,因此内存泄漏.

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

谢谢你的建议!

更多信息

TContainingObject提供一个Array属性来访问Array中IMyInterface包含的各个实例.

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

Rem*_*eau 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;
Run Code Online (Sandbox Code Playgroud)

.

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;
Run Code Online (Sandbox Code Playgroud)

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

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;
Run Code Online (Sandbox Code Playgroud)

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

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;
Run Code Online (Sandbox Code Playgroud)

.

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;
Run Code Online (Sandbox Code Playgroud)