Таинственный случай неожиданной неявной переменной интерфейса

Недавно я столкнулся с некоторым поведением, которое я просто не мог и не могу объяснить, связанное с интерфейсными переменными Delphi.

По сути, это сводится к неявная интерфейсная переменная, которую компилятор генерирует в методе Broadcast .

В инструкции end, завершающей метод, код эпилога содержит два вызова IntfClear . Один из которых я могу объяснить, он соответствует локальной переменной Listener . Другой я не могу объяснить, и он приведет вас к TComponent._Release (отладка DCU) после того, как экземпляр объекта был уничтожен. Это не приводит к AV, но это просто удача, а при полной отладке FastMM сообщается о доступе к экземпляру после уничтожения.

Вот код:

program UnexpectedImplicitInterfaceVariable;

{$APPTYPE CONSOLE}

uses
  SysUtils, Classes;

type
  IListener = interface
    ['{6D905909-98F6-442A-974F-9BF5D381108E}']
    procedure HandleMessage(Msg: Integer);
  end;

  TListener = class(TComponent, IListener)
  //TComponent._AddRef and TComponent_Release return -1
  private
    procedure HandleMessage(Msg: Integer);
  end;

{ TListener }

procedure TListener.HandleMessage(Msg: Integer);
begin
end;

type
  TBroadcaster = class
  private
    FListeners: IInterfaceList;
    FListener: TListener;
  public
    constructor Create;
    procedure Broadcast(Msg: Integer);
  end;

constructor TBroadcaster.Create;
begin
  inherited;
  FListeners := TInterfaceList.Create;
  FListener := TListener.Create(nil);
  FListeners.Add(FListener);
end;

procedure TBroadcaster.Broadcast(Msg: Integer);
var
  i: Integer;
  Listener: IListener;
begin
  for i := 0 to FListeners.Count-1 do
  begin
    Listener := FListeners[i] as IListener;
    Listener.HandleMessage(Msg);
  end;
  Listener := nil;

  FListeners.Clear;
  FreeAndNil(FListener);
end;//method epilogue: why is there a call to IntfClear and then TComponent._Release?

begin
  with TBroadcaster.Create do
  begin
    Broadcast(42);
    Free;
  end;
end.

И вот разборка эпилога:

enter image description here

Вот, ясно, как день, два вызова IntfClear.

] Итак, кто может увидеть очевидное объяснение того, что мне не хватает?


ОБНОВЛЕНИЕ

Что ж, Уве сразу понял. FListeners [i] нуждается во временной неявной переменной для своей переменной результата. Я не видел этого, поскольку назначал Listener , но, конечно, это другая переменная.

Следующий вариант является явным представлением того, что компилятор генерирует для моего исходного кода.

procedure TBroadcaster.Broadcast(Msg: Integer);
var
  i: Integer;
  Intf: IInterface;
  Listener: IListener;
begin
  for i := 0 to FListeners.Count-1 do
  begin
    Intf := FListeners[i];
    Listener := Intf as IListener;
    Listener.HandleMessage(Msg);
  end;
  Listener := nil;

  FListeners.Clear;
  FreeAndNil(FListener);
end;

Когда написано таким образом, очевидно, что Intf нельзя очистить до эпилога.

8
задан David Heffernan 18 March 2011 в 16:22
поделиться