为了提供尽可能多的信息,这是我正在做的非常基本的示例

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数组中。

调用TContainingObjectdestructor时,它是零引用,并且折叠Array。

我的问题是,在没有其他引用的情况下,从未调用TMyInterfacedObjectdestructor,从而导致内存泄漏。

我是在做错什么,还是Delphi的引用计数系统不能应付接口类型数组中保存的接口对象的简单概念?

感谢您的任何建议!

更多信息

TContainingObject提供Array属性以访问Array中包含的IMyInterface的各个实例。

在我的实际代码中,多个接口类型之间都有循环引用。
我们建议IMyInterface包含函数GetSomething: IAnotherInterface,而IAnotherInterface包含GetMyInterface: IMyInterface(循环引用)。
这可能引起我的问​​题吗?如果是这样,则绝对需要循环引用,那么考虑到该解决方案将会是什么?

最佳答案

如果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;

关于arrays - 在数组中保存时释放接口(interface)对象的问题,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/9985394/

10-09 19:27