为什么这个接口委托会导致内存泄漏?
Why is this interface delegation causing memory leak?
假设我有以下代码
单位TalkerIntf.pas
unit TalkerIntf;
interface
{$MODE OBJFPC}
type
ITalker = interface
['{95E1FAE3-7495-4404-AE88-6A7DB88383EC}']
procedure say() ;
end;
implementation
end.
单位TalkerImpl.pas
unit TalkerImpl;
interface
{$MODE OBJFPC}
uses
TalkerIntf;
type
TTalker = class(TInterfacedObject, ITalker)
public
procedure say();
end;
implementation
procedure TTalker.say();
begin
writeln('Hello');
end;
end.
单位代表TalkerImpl.pas
unit DelegateTalkerImpl;
interface
{$MODE OBJFPC}
uses
TalkerIntf;
type
TDelegateTalker = class(TInterfacedObject, ITalker)
private
fActualTalker : ITalker;
public
constructor create(const talker : ITalker);
destructor destroy(); override;
property talker : ITalker read fActualTalker implements ITalker;
end;
implementation
constructor TDelegateTalker.create(const talker : ITalker);
begin
fActualTalker := talker;
end;
destructor TDelegateTalker.destroy();
begin
fActualTalker := nil;
inherited destroy();
end;
end.
和程序memleak.pas
program memleak;
{$MODE OBJFPC}
uses
TalkerIntf,
TalkerImpl,
DelegateTalkerImpl;
var
talker : ITalker;
begin
talker := TDelegateTalker.create(TTalker.create());
talker.say();
end.
在 (-gh
) 上使用 FreePascal 3.0.4 和 heaptrc 进行编译,heaptrc 报告存在内存泄漏
$ fpc -gh memleak.pas
$ ./memleak
Heaptrc 输出
Hello
Heap dump by heaptrc unit
2 memory blocks allocated : 64/64
0 memory blocks freed : 0/0
2 unfreed memory blocks : 64
True heap size : 32768
True free heap : 32384
Should be : 32448
Call trace for block [=15=]007FA0D7846180 size 32
[=15=]0000000040020F
Call trace for block [=15=]007FA0D78460C0 size 32
为什么这个接口委托会导致内存泄漏?如何避免?
更新
似乎唯一的解决方法是删除 implements
并手动进行委派。以下代码不会发生内存泄漏。
unit DelegateTalkerImpl;
interface
{$MODE OBJFPC}
uses
TalkerIntf;
type
TDelegateTalker = class(TInterfacedObject, ITalker)
private
fActualTalker : ITalker;
public
constructor create(const talker : ITalker);
destructor destroy(); override;
procedure say();
end;
implementation
constructor TDelegateTalker.create(const talker : ITalker);
begin
fActualTalker := talker;
end;
destructor TDelegateTalker.destroy();
begin
fActualTalker := nil;
inherited destroy();
end;
procedure TDelegateTalker.say();
begin
fActualTalker.say();
end;
end.
When you define a variable as ITalker, the newly created object is not returned, but only the field that implements it. As a result, a newly created object is leaked.
Delphi's behavior is the same.
为了避免内存泄漏但仍然使用implements
关键字,我们需要分配给ITalker
以外类型的临时变量,然后将其类型转换为ITalker
.
var
talker : ITalker;
delegateTalker : IInterface;
begin
delegateTalker := TDelegateTalker.create(TTalker.create());
talker := delegateTalker as ITalker;
talker.say();
end.
假设我有以下代码
单位TalkerIntf.pas
unit TalkerIntf;
interface
{$MODE OBJFPC}
type
ITalker = interface
['{95E1FAE3-7495-4404-AE88-6A7DB88383EC}']
procedure say() ;
end;
implementation
end.
单位TalkerImpl.pas
unit TalkerImpl;
interface
{$MODE OBJFPC}
uses
TalkerIntf;
type
TTalker = class(TInterfacedObject, ITalker)
public
procedure say();
end;
implementation
procedure TTalker.say();
begin
writeln('Hello');
end;
end.
单位代表TalkerImpl.pas
unit DelegateTalkerImpl;
interface
{$MODE OBJFPC}
uses
TalkerIntf;
type
TDelegateTalker = class(TInterfacedObject, ITalker)
private
fActualTalker : ITalker;
public
constructor create(const talker : ITalker);
destructor destroy(); override;
property talker : ITalker read fActualTalker implements ITalker;
end;
implementation
constructor TDelegateTalker.create(const talker : ITalker);
begin
fActualTalker := talker;
end;
destructor TDelegateTalker.destroy();
begin
fActualTalker := nil;
inherited destroy();
end;
end.
和程序memleak.pas
program memleak;
{$MODE OBJFPC}
uses
TalkerIntf,
TalkerImpl,
DelegateTalkerImpl;
var
talker : ITalker;
begin
talker := TDelegateTalker.create(TTalker.create());
talker.say();
end.
在 (-gh
) 上使用 FreePascal 3.0.4 和 heaptrc 进行编译,heaptrc 报告存在内存泄漏
$ fpc -gh memleak.pas
$ ./memleak
Heaptrc 输出
Hello
Heap dump by heaptrc unit
2 memory blocks allocated : 64/64
0 memory blocks freed : 0/0
2 unfreed memory blocks : 64
True heap size : 32768
True free heap : 32384
Should be : 32448
Call trace for block [=15=]007FA0D7846180 size 32
[=15=]0000000040020F
Call trace for block [=15=]007FA0D78460C0 size 32
为什么这个接口委托会导致内存泄漏?如何避免?
更新
似乎唯一的解决方法是删除 implements
并手动进行委派。以下代码不会发生内存泄漏。
unit DelegateTalkerImpl;
interface
{$MODE OBJFPC}
uses
TalkerIntf;
type
TDelegateTalker = class(TInterfacedObject, ITalker)
private
fActualTalker : ITalker;
public
constructor create(const talker : ITalker);
destructor destroy(); override;
procedure say();
end;
implementation
constructor TDelegateTalker.create(const talker : ITalker);
begin
fActualTalker := talker;
end;
destructor TDelegateTalker.destroy();
begin
fActualTalker := nil;
inherited destroy();
end;
procedure TDelegateTalker.say();
begin
fActualTalker.say();
end;
end.
When you define a variable as ITalker, the newly created object is not returned, but only the field that implements it. As a result, a newly created object is leaked. Delphi's behavior is the same.
为了避免内存泄漏但仍然使用implements
关键字,我们需要分配给ITalker
以外类型的临时变量,然后将其类型转换为ITalker
.
var
talker : ITalker;
delegateTalker : IInterface;
begin
delegateTalker := TDelegateTalker.create(TTalker.create());
talker := delegateTalker as ITalker;
talker.say();
end.