为什么子类中的接口未发布

时间:2016-06-11 08:06:02

标签: delphi delphi-xe7

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

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;

将来自FDatabase的{​​{1}}创建为TManager并传递给TDatabase的构造函数,因此它在TTools中具有相同的(?)对象/接口在TManager。 然后TTools泄漏内存,因为子类中的接口/对象(lExample)。为什么不发布界面?或者我对Delphi基础知识有什么不了解?

1 个答案:

答案 0 :(得分:2)

  

为什么不发布界面?

您有循环参考 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]属性将触发Delphi在分配FTools时生成不同的代码。并且运行时将保留一些簿记,以便即使接口的引用计数变为零,如果弱参考发生参与循环引用,对象也不会被销毁。
MarcoCantù在这里写道:http://blog.marcocantu.com/blog/2016-april-weak-unsafe-interface-references.html
他还写了关于[unsafe]属性的文章。不要使用那个,除非你确切地知道它意味着什么,它不是你需要的。

您应该只将其中一个循环引用标记为[weak]!如果你标记两个意外都会发生。

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

按照http://blog.dummzeuch.de/2014/06/19/weak-references-or-why-you-should-enable-reportmemoryleaksonshutdown/

所述使用此hack
    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;

这样做是以不正当的方式引用,以使引用计数不会永久上升 并不是说这个黑客没有对[weak]属性给出的意外事件提供全面保护 如果您的弱引用恰好参与循环引用,那么您可能会过早销毁FParent。

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