在Delphi XE中,以下代码将导致内存泄漏:
procedure TForm1.Button1Click(Sender: TObject);
var P, B: TProc;
begin
B := procedure
begin
end;
P := procedure
begin
B;
end;
end;
使用
运行代码ReportMemoryLeaksOnShutdown := True;
和内存管理器提示:
21-28 bytes: TForm1.Button1Click$ActRec x 1
答案 0 :(得分:14)
这是由于匿名方法的工作方式。匿名方法是作为TInterfacedObject后代实现的,如果在同一例程中有多个,they end up as two methods of the same object.它使用接口进行引用计数,这样就不会泄漏对象。但是,if an anonymous method references itself, that ends up throwing off the reference count导致内存泄漏。你在这里看到的是这两件事的结合。
答案 1 :(得分:13)
这是编译器中的一个错误(据我所知)。我在Embarcadero质量中心开设了QC83259。
您可以通过在例程中创建匿名过程来解决此错误。以下代码不会泄漏。
procedure TForm1.Button1Click(Sender: TObject);
var P, B: TProc;
begin
B := GetMethod(); //Note, the "()" are necessary in this situation.
P := procedure
begin
B;
end;
end;
function TForm1.GetMethod: TProc;
begin
Result := procedure
begin
end;
end;
答案 2 :(得分:3)
我知道我这次讨论迟了2年,但是我最近在代码中遇到了这个内存泄漏,我无法得到Ken的建议答案。因此,在我的同事的帮助下,我们提出了一个不同的答案,以继续使用嵌套的匿名方法,但避免任何内存泄漏。
以下是我们找到的解决方案示例:
procedure TForm1.Button1Click(Sender: TObject);
var P, B: TProc;
begin
B := procedure
begin
end;
P := procedure
begin
B;
end;
B := nil;
end;
我的信念是,由于局部变量的约束方式是为了扩展它们的生命,以便匿名方法在它创建的范围之外使用它,它正在制作底层接口的副本对象,以便将变量从堆栈移动到堆,并且这样做是调用AddRef,它会增加引用计数器。在使用变量后将变量设置为nil会调用Release,而Release又将参考计数器减少到0,从而允许释放接口对象。
执行此操作后,我们没有看到之前发生的内存泄漏。
这是否是一个错误,我无法回答,但我有兴趣听取别人的意见。我们认为这是一种允许我们以这样的嵌套形式继续使用匿名方法的方法。