delphi - 意想不到的隐式接口(interface)变量的神秘案例

标签 delphi interface

我最近遇到了一些与 Delphi 接口(interface)变量相关的行为,我根本无法也无法解释。

本质上,它归结为编译器在 Broadcast 方法中生成的隐式接口(interface)变量。

在终止该方法的结束语句中,尾声代码包含两次对 IntfClear 的调用。我可以解释其中之一,它对应于Listener局部变量。另一个我无法解释,它会在对象实例被销毁后将您带到TComponent._Release(调试DCU)。它不会导致反病毒,但这只是幸运的,并且通过完整的 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 的调用。

那么,谁能看到我缺少的明显解释?

<小时/>

更新

嗯,乌韦立刻就明白了。 FLListeners[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 直到尾声才能被清除。

最佳答案

只是猜测,但也许 FListeners[i] 作为 IListener 使用了 FListeners[i] 的临时变量。毕竟它是函数调用的结果。

关于delphi - 意想不到的隐式接口(interface)变量的神秘案例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5354696/

相关文章:

database - ClientDataSet 中的 "key violation"自动增量字段 [Delphi]

c# - 虚拟馆藏界面

Symfony 4.4 翻译界面问题

macos - Delphi Berlin 10.1 创建文件/文件夹 OS X

regex - 如何替换正则表达式匹配中的字符串

Golang 参数转换

Java 接口(interface)和模板的混淆

c# - Microsoft 命名指南 - 什么是 "private interface members"?

javascript - Indy 可以运行 Javascript 吗?

xml - 在 Delphi 中生成 XML 时如何编写 XML?