this is a constructed example. I don't want to post the original code here. I tried to extract the relevant parts though.
I have an interface that manages a list of listeners.
TListenerProc = reference to procedure (SomeInt : ISomeInterface);
ISomeInterface = interface
   procedure AddListener (Proc : TListenerProc);   
end;
Now I register a listener:
SomeObj.AddListener (MyListener);
procedure MyListener (SomeInt : ISomeInterface);
begin
  ExecuteSynchronized (procedure
                       begin
                       DoSomething (SomeInt);
                       end);
end;
I do get memory leaks. Both the anonymous method and the interfaces are never freed. I suspect that this is due to some kind of circular reference here. The anonymous method keeps the interface alife and the interface keeps the anonymous method alife.
Two questions:
- Do you support that explanation? Or am I missing something else here?
- Is there anything I can do about it?
Thanks in advance!
EDIT: It's not so easy to reproduce this in an application small enough to post it here. The best I can do by now is the following. The anonymous method does not get released here:
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;开发者_如何学Go
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.
Your code is far from minimal. The following:
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.
has the very same problem (Delphi 2009 here). This can't be worked or designed around. Looks to me like a bug in the compiler.
Edit:
Or maybe this is a problem of the memory leak detection. It has nothing to do with the parameter being an interface, a parameterless procedure leads to the same "leak". Very strange.
Looks to me like a definite circular reference issue.  Anonymous methods are managed through hidden interfaces, and if the TList<TListenerProc> is owned by the object that ISomeInterface is implemented on, then you've got a circular reference issue.
One possible solution would be to put a ClearListeners method on ISomeInterface which calls .Clear on the TList<TListenerProc>.  As long as nothing else is holding a reference to the anonymous methods, that would make them all vanish and drop their references to the ISomeInterface.
I've done a few articles about the structure and implementation of anonymous methods that might help you understand what you're really working with and how they operate a little bit better. You can find them at http://tech.turbu-rpg.com/category/delphi/anonymous-methods.
The problem is with anonymous methods in the dpr main.
Just put your code in a routine and call that in the dpr main and the memory leak report is gone.
procedure Main;
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;
begin
  Main;
end.
 
         
                                         
                                         
                                         
                                        ![Interactive visualization of a graph in python [closed]](https://www.devze.com/res/2023/04-10/09/92d32fe8c0d22fb96bd6f6e8b7d1f457.gif) 
                                         
                                         
                                         
                                         加载中,请稍侯......
 加载中,请稍侯......
      
精彩评论