tipo puede metodos instrucciones indicado funciones funcion flecha expresiones expresion ejemplos delegados delegado convertir anonimos delphi memory-management interface delphi-2010 anonymous-methods

delphi - puede - metodos anonimos c#



Interfaces, métodos anónimos y fugas de memoria (3)

El problema es con los métodos anónimos en la página principal de dpr.

Simplemente ponga su código en una rutina y llámelo al dpr main y el informe de pérdida de memoria se habrá ido.

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.

Este es un ejemplo construido. No quiero publicar el código original aquí. Aunque traté de extraer las partes relevantes.

Tengo una interfaz que gestiona una lista de oyentes.

TListenerProc = reference to procedure (SomeInt : ISomeInterface); ISomeInterface = interface procedure AddListener (Proc : TListenerProc); end;

Ahora registro a un oyente:

SomeObj.AddListener (MyListener); procedure MyListener (SomeInt : ISomeInterface); begin ExecuteSynchronized (procedure begin DoSomething (SomeInt); end); end;

Tengo fugas de memoria. Tanto el método anónimo como las interfaces nunca se liberan. Sospecho que esto se debe a algún tipo de referencia circular aquí. El método anónimo mantiene la interfaz alife y la interfaz mantiene el método anónimo alife.

Dos preguntas:

  1. ¿Apoyas esa explicación? ¿O me estoy perdiendo algo más aquí?
  2. ¿Hay algo que pueda hacer al respecto?

¡Gracias por adelantado!

EDITAR : No es tan fácil reproducir esto en una aplicación lo suficientemente pequeña como para publicarlo aquí. Lo mejor que puedo hacer ahora es lo siguiente. El método anónimo no se libera aquí:

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.


Me parece un tema de referencia circular definido. Los métodos anónimos se administran a través de interfaces ocultas, y si TList<TListenerProc> es propiedad del objeto en el que se implementa ISomeInterface, entonces tiene un problema de referencia circular.

Una posible solución sería poner un método ClearListeners en ISomeInterface que llame a .Clear en el TList<TListenerProc> . Mientras que nada más contenga una referencia a los métodos anónimos, eso hará que todos desaparezcan y eliminen sus referencias al ISomeInterface.

He publicado algunos artículos sobre la estructura y la implementación de métodos anónimos que pueden ayudarlo a comprender con qué está trabajando realmente y cómo funcionan un poco mejor. Puede encontrarlos en http://tech.turbu-rpg.com/category/delphi/anonymous-methods .


Su código está lejos de ser mínimo. El seguimiento:

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.

tiene el mismo problema (Delphi 2009 aquí). Esto no puede ser trabajado o diseñado alrededor. Me parece un error en el compilador.

Editar:

O tal vez este es un problema de la detección de fugas de memoria. No tiene nada que ver con que el parámetro sea una interfaz, un procedimiento sin parámetros conduce a la misma "fuga". Muy extraño.