为什么子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
我遇到了以下情况,我想知道这段代码是否泄漏了内存。 假设我有以下接口和实现:
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