加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

delphi – 意外的隐式接口变量的神秘案例

发布时间:2020-12-15 03:51:56 所属栏目:大数据 来源:网络整理
导读:我最近遇到了一些我无法解释的行为,与Delphi接口变量有关. 从本质上讲,它归结为编译器在Broadcast方法中生成的隐式接口变量. 在终止方法的结束语句中,结尾代码包含对IntfClear的两次调用.其中一个我可以解释,它对应于Listener局部变量.另一个我无法解释,它会
我最近遇到了一些我无法解释的行为,与Delphi接口变量有关.

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

在终止方法的结束语句中,结尾代码包含对IntfClear的两次调用.其中一个我可以解释,它对应于Listener局部变量.另一个我无法解释,它会在对象实例被销毁后将你带到TComponent._Release(Debug DCUs).它不会产生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.

以下是结语的反汇编:

在那里,明确的是对IntfClear的两次调用.

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

UPDATE

好吧,Uwe马上得到它. 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无法在结尾之前被清除.

解决方法

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

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读