意外隐式接口变量的神秘案例 [英] The mysterious case of the unexpected implicit interface variable

查看:16
本文介绍了意外隐式接口变量的神秘案例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近遇到了一些我无法解释也无法解释的行为,这些行为与 Delphi 接口变量有关.

I recently came across some behaviour that I simply could not and cannot explain, related to Delphi interface variables.

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

Essentially, it boils down to an implicit interface variable that the compiler generates in the Broadcast method.

在终止方法的结束语句中,尾声代码包含对 IntfClear 的两次调用.其中之一我可以解释,它对应于 Listener 局部变量.另一个我无法解释,它会在对象实例被销毁后将您带到 TComponent._Release(调试 DCU).它不会导致 AV,但这只是幸运,并且使用完整的 FastMM 调试报告销毁后实例访问.

At the end statement that terminates the method, the epilogue code contains two calls to IntfClear. One of which I can explain, it corresponds to the Listener local variable. The other one I cannot explain and it takes you to TComponent._Release (Debug DCUs) after the object instance has been destroyed. It doesn't result in an AV, but that's just lucky, and with full FastMM debug a post-destruction instance access is reported.

代码如下:

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.

这是结尾的反汇编:

显然,这是对 IntfClear 的两次调用.

There, clear as day, are the two calls to IntfClear.

那么,谁能看到我遗漏的明显解释?

So, who can see the obvious explanation that I am missing?

更新

嗯,Uwe 马上就知道了.FListeners[i] 需要一个临时隐式变量作为​​其结果变量.我没有看到,因为我正在分配给 Listener,但当然这是一个不同的变量.

Well, Uwe got it straight away. FListeners[i] needs a temporary implicit variable for its result variable. I didn't see that since I was assigning to Listener, but of course that's a different variable.

以下变体是编译器为我的原始代码生成的内容的显式表示.

The following variant is an explicit representation of what the compiler is generating for my original code.

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直到尾声才能被清除.

When written this way it is obvious that Intf cannot be cleared until the epilogue.

推荐答案

只是猜测,但也许 FListeners[i] as IListener 使用了 FListeners[i]<的临时变量/代码>.毕竟是函数调用的结果.

Just a guess, but perhaps the FListeners[i] as IListener uses a temporary variable for FListeners[i]. After all it is the result of a function call.

这篇关于意外隐式接口变量的神秘案例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆