这是一个构建的例子。我不想在这里发布原始代码。我试图提取相关的部分。
我有一个接口来管理一个监听器列表。
TListenerProc = reference to procedure (SomeInt : ISomeInterface); ISomeInterface = interface procedure AddListener (Proc : TListenerProc); end;
现在我注册一个监听器:
SomeObj.AddListener (MyListener); procedure MyListener (SomeInt : ISomeInterface); begin ExecuteSynchronized (procedure begin DoSomething (SomeInt); end); end;
我确实有内存泄漏。匿名方法和接口都不会被释放。我怀疑这是因为这里的某种循环引用。匿名方法保持接口alife和接口保持匿名方法alife。
两个问题:
你支持这个解释吗?还是我在这里遗漏了别的东西?
有什么可以做的吗?
提前致谢!
编辑:在一个足够小的应用程序中再现这个并不容易,可以在这里发布。现在我能做的最好的就是如下。匿名方法不会在这里发布:
program TestMemLeak; {$APPTYPE CONSOLE} uses Generics.Collections,SysUtils; type ISomeInterface = interface; TListenerProc = reference to procedure (SomeInt : ISomeInterface); ISomeInterface = interface ['{DB5A336B-3F79-4059-8933-27699203D1B6}'] procedure AddListener (Proc : TListenerProc); procedure NotifyListeners; procedure Test; end; TSomeInterface = class (TInterfacedObject,ISomeInterface) strict private FListeners : TList <TListenerProc>; protected procedure AddListener (Proc : TListenerProc); procedure NotifyListeners; procedure Test; public constructor Create; destructor Destroy; override; end; procedure TSomeInterface.AddListener(Proc: TListenerProc); begin FListeners.Add (Proc); end; constructor TSomeInterface.Create; begin FListeners := TList <TListenerProc>.Create; end; destructor TSomeInterface.Destroy; begin FreeAndNil (FListeners); inherited; end; procedure TSomeInterface.NotifyListeners; var Listener : TListenerProc; begin for Listener in FListeners do Listener (Self); end; procedure TSomeInterface.Test; begin // do nothing end; procedure Execute (Proc : TProc); begin Proc; end; procedure MyListener (SomeInt : ISomeInterface); begin Execute (procedure begin SomeInt.Test; end); end; var Obj : ISomeInterface; begin try ReportMemoryLeaksOnShutdown := True; Obj := TSomeInterface.Create; Obj.AddListener (MyListener); Obj.NotifyListeners; Obj := nil; except on E: Exception do Writeln(E.ClassName,': ',E.Message); end; end.
解决方法
你的代码远不是最小的。下列:
program AnonymousMemLeak; {$APPTYPE CONSOLE} uses SysUtils; type TListenerProc = reference to procedure (SomeInt : IInterface); procedure MyListener (SomeInt : IInterface); begin end; var Listener: TListenerProc; begin try ReportMemoryLeaksOnShutdown := True; Listener := MyListener; Listener := nil; except on E: Exception do Writeln(E.ClassName,E.Message); end; end.
有同样的问题(Delphi 2009这里)。这不能在周围工作或设计。看起来像编译器中的一个错误。
编辑:
或者也许这是内存泄漏检测的问题。它与参数是一个接口无关,无参数的过程导致相同的“泄漏”。很奇怪。