为什么子class中的一个接口没有释放

Why is an interface in the sub class not released

我遇到了以下情况,我想知道这段代码是否泄漏了内存。 假设我有以下接口和实现:

type
ITools = interface
  function HelloWorld : String;
end;

IDatabase = interface
  function Query(AQuery : String) : String;
end;

IManager = interface
  procedure Execute;
end;

TDatabase = class(TInterfacedObject, IDatabase)
  strict private
    FTools : ITools;
  public
    constructor Create;
    destructor Destroy; override;
    function Query(AQuery : String) : String;
end;

TTools = class(TInterfacedObject, ITools)
  strict private
    FDatabase : IDatabase;
  public
    constructor Create(ADatabase : IDatabase);
    destructor Destroy; override;
    function HelloWorld : String;
end;

TManager = class(TInterfacedObject, IManager)
  strict private
    FDatabase : IDatabase;
  public
    constructor Create;
    procedure Execute;
end; 

现在,如果您创建这个:

procedure Example;
var 
  lExample : IManager;
begin
  lExample := TManager.Create;
  lExample.Execute;
  lExampe := nil; // Should not be necessary
end;

其中 TManager 中的 FDatabase 被创建为 TDatabase 并传递给 TTools 的构造函数,因此它具有相同的(?)对象/接口TManager 中的 TTools。 然后 lExample 泄漏内存,因为子 class (IDatabase) 中的接口/对象。为什么不发布接口?或者我不了解 Delphi 基础知识?

Why isn't it the interface released?

你有一个循环引用。
TDatabase 包含对 TTools 的引用,TTools 包含对 TDatabase 的引用。
因为 Delphi 没有垃圾收集器,所以无法在没有帮助的情况下解决这些循环引用。

如果您使用的是 Mobile NexGen 编译器或 D10.1 Berlin,解决方案是将 TDatabase 声明为:

TDatabase = class(TInterfacedObject, IDatabase)
  strict private
    [weak]          <<--
    FTools : ITools;
  public
    constructor Create;
    destructor Destroy; override;
    function Query(AQuery : String) : String;
end;

[weak]属性在赋值FTools时会触发Delphi生成不同的代码。并且运行时将保留一些簿记,以便即使接口的引用计数变为零,如果弱引用恰好 not 参与循环引用,对象也不会被销毁。
Marco Cantù 在这里写道:http://blog.marcocantu.com/blog/2016-april-weak-unsafe-interface-references.html
他还写了关于 [unsafe] 属性的文章。不要使用那个,除非您确切地知道它的含义,否则它不是您所需要的。

您应该只将其中一个循环引用标记为 [weak]!如果你标记这两个不幸事件将会发生。

编译器不支持[weak]怎么办?
如果您对 Windows 或 OSX 目标使用较旧的 Delphi,解决方案如下。

按照以下描述使用此技巧:http://blog.dummzeuch.de/2014/06/19/weak-references-or-why-you-should-enable-reportmemoryleaksonshutdown/

    procedure SetWeak(_InterfaceField: PIInterface; const _Value: IInterface);
    begin
      PPointer(_InterfaceField)^ := Pointer(_Value);
    end;

    type
      TChild = class(TInterfacedObject, IChild)
      private
        FParent: IParent; // This must be a weak reference!
      public
        constructor Create(Parent: IParent);
        destructor Destroy; override;
      end;

    constructor TChild.Create(Parent: IParent);
    begin
      inherited Create;
      SetWeak(@FParent, Parent);
    end;

    destructor TChild.Destroy;
    begin
      SetWeak(@FParent, Nil);
      inherited;
    end;

这样做是以一种卑鄙的方式进行引用,这样引用计数就不会永久增加。
并不是说此 hack 没有 [weak] 属性提供的针对事故的全面保护。
如果您的弱引用碰巧 参与循环引用,那么您可能会过早破坏 FParent。

Arnaud Bouchez 在他的博客中有更详细的文章;我推荐你阅读它:http://blog.synopse.info/post/2012/06/18/Circular-reference-and-zeroing-weak-pointers