我面临以下情况,我想知道这段代码是在泄漏内存。 假设我有以下接口和实现:
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基础知识有什么不了解?
答案 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目标,则解决方案如下。
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