【问题标题】:Why is an interface in the sub class not released为什么子类中的接口没有释放
【发布时间】:2016-06-11 08:06:02
【问题描述】:

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

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 的构造函数,因此它在TTools 中与在TManager 中具有相同的(?)对象/接口。 然后lExample 泄漏内存,因为子类中的接口/对象(IDatabase)。为什么不发布接口?或者我对 Delphi 基础知识有哪些不了解的地方?

【问题讨论】:

标签: delphi delphi-xe7


【解决方案1】:

为什么不发布界面?

你有一个循环引用。
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 生成不同的代码。并且运行时将保留一些簿记,以便即使接口的引用计数变为零,如果弱引用碰巧参与循环引用,对象也不会被破坏。
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/ 的描述使用此 hack

 过程 SetWeak(_InterfaceField: PIInterface; const _Value: IInterface);
    开始
      PPointer(_InterfaceField)^ := 指针(_Value);
    结尾;

    类型
      TChild = 类(TInterfacedObject,IChild)
      私人的
        FParent:IPParent; // 这一定是弱引用!
      上市
        构造函数创建(父级:IParent);
        析构函数销毁;覆盖;
      结尾;

    构造函数 TChild.Create(Parent: IParent);
    开始
      继承创建;
      SetWeak(@FParent, 父母);
    结尾;

    析构函数 TChild.Destroy;
    开始
      SetWeak(@FParent, Nil);
      遗传;
    结尾;

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-12-01
    • 2014-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-14
    • 2017-08-07
    • 2018-10-24
    相关资源
    最近更新 更多