Интерфейсы, анонимные методы и утечки памяти

это - созданный пример. Я не хочу отправлять исходный код здесь. Я пытался извлечь соответствующие части все же.

У меня есть интерфейс, который управляет списком слушателей.

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.

Два вопроса:

  1. Вы поддерживаете то объяснение? Или я пропускаю что-то еще здесь?
  2. Есть ли что-нибудь, что я могу делать с этим?

Заранее спасибо!


Править: Не настолько легко воспроизвести это в приложении, достаточно маленьком для регистрации его здесь. Лучшее, которое я могу сделать к настоящему времени, следующее. Анонимный метод не становится выпущенным здесь:

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.
17
задан jpfollenius 16 February 2010 в 14:56
поделиться

2 ответа

Ваш код далек от минимума. Следующее:

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). С этим нельзя работать или спроектировать. Мне кажется, это ошибка в компиляторе.

Edit:

Или, может быть, это проблема обнаружения утечки памяти. Это не имеет ничего общего с параметром, являющимся интерфейсом, процедура без параметров приводит к той же «утечке». Очень странно.

8
ответ дан 30 November 2019 в 14:28
поделиться

Мне кажется, что это определенно проблема круговой ссылки. Анонимные методы управляются через скрытые интерфейсы, и если TList принадлежит объекту, на котором реализован ISomeInterface, то у вас проблема с круговой ссылкой.

Одним из возможных решений может быть размещение метода ClearListeners на ISomeInterface, который вызывает .Clear на TList. Пока ничто другое не держит ссылки на анонимные методы, это заставит их все исчезнуть и отбросить их ссылки на ISomeInterface.

Я написал несколько статей о структуре и реализации анонимных методов, которые могут помочь вам лучше понять, с чем вы на самом деле работаете и как они работают. Вы можете найти их на http://tech.turbu-rpg.com/category/delphi/anonymous-methods.

3
ответ дан 30 November 2019 в 14:28
поделиться
Другие вопросы по тегам:

Похожие вопросы: