为什么基于TComponent泄漏内存的接口实现?

mjn*_*mjn 18 delphi memory-leaks interface delphi-2009

此Delphi代码将显示TMyImplementation实例的内存泄漏:

program LeakTest;

uses
  Classes;

type
  MyInterface = interface
  end;

  TMyImplementation = class(TComponent, MyInterface)
  end;

  TMyContainer = class(TObject)
  private
    FInt: MyInterface;
  public
    property Impl: MyInterface read FInt write FInt;
  end;

var
  C: TMyContainer;
begin
  ReportMemoryLeaksOnShutdown := True;

  C := TMyContainer.Create;
  C.Impl := TMyImplementation.Create(nil);
  C.Free;
end.
Run Code Online (Sandbox Code Playgroud)

如果TComponent被TInterfacedObject替换并且构造函数更改为Create(),则泄漏消失.与TComponent有什么不同?

非常感谢答案.总结一下:说"如果你使用接口,它们是引用计数,因此它们可以为你释放,这很容易,但却是错误的." - 实际上任何实现接口的类都可以破坏这个规则.(并且不会显示编译器提示或警告.)

Lie*_*ers 28

实施方面的差异

  • TComponent._Release 不会释放您的实例.
  • TInterfacedObject._Release 确实释放你的实例.

也许有人可以插入,但我对此的看法是,这TComponent并不意味着我们通常使用接口的方式用作引用计数对象.

TComponent._Release的实现

function TComponent._Release: Integer;
begin
  if FVCLComObject = nil then
    Result := -1   // -1 indicates no reference counting is taking place
  else
    Result := IVCLComObject(FVCLComObject)._Release;
end;
Run Code Online (Sandbox Code Playgroud)

  • 正确!TComponent的直播通常由其所有者共同控制. (6认同)

Rob*_*edy 20

TComponent没有像TInterfacedObject那样实现其_AddRef和_Release方法.它将其引用计数推迟到其VCLComObject属性,该属性应该是其他一些接口对象.由于TComponent不计算引用,因此无法检测其引用计数何时达到零,因此它不会释放自身.

VCLComObject属性包含一个接口引用,它应该实现IVCLComObject.如果组件的关联VCLComObject对象已被告知它拥有该组件,那么当该接口的引用计数达到零时,它将销毁其关联的组件.它告诉它通过调用FreeOnRelease方法拥有该组件.

所有这些都旨在使VCL组件更容易包装到COM对象中.如果这不是您的目标,那么您可能会在此过程中与其他几个意想不到的设计方面进行斗争,因此您可能希望重新评估您使组件首先实现接口的动机.


ang*_*son 6

一个组件应该由其他东西拥有和销毁,通常是一种形式.在该场景中,不使用引用计数.如果将组件作为接口引用传递,那么在方法返回时它被销毁将是非常不幸的.

因此,TComponent中的引用计数已被删除.