意外的隐含接口变量的神秘情况 [英] The mysterious case of the unexpected implicit interface variable

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

问题描述

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



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



在结束方法的结束语句中,结尾代码包含两个调用 IntfClear 。其中一个我可以解释,它对应于 Listener 局部变量。另一个我不能解释,在对象实例被销毁后,它需要你到$ code> TComponent._Release (调试DCU)。它不会导致AV,但这只是幸运的,并且完整的FastMM调试报告了破坏后的实例访问。



这里是代码:

 程序UnexpectedImplicitInterfaceVariable; 

{$ APPTYPE CONSOLE}

使用
SysUtils,Classes;

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

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

{TListener}

程序TListener.HandleMessage(Msg:Integer);
begin
end;

type
TBroadcaster = class
private
FListeners:IInterfaceList;
FListener:TListener;
public
构造函数创建;
procedure Broadcast(Msg:Integer);
结束

构造函数TBroadcaster.Create;
开始
继承;
FListeners:= TInterfaceList.Create;
FListener:= TListener.Create(nil);
FListeners.Add(FListener);
结束

程序TBroadcaster.Broadcast(Msg:Integer);
var
i:整数;
侦听器:IListener;
begin
for i:= 0 to FListeners.Count-1 do
begin
Listener:= FListeners [i] as IListener;
Listener.HandleMessage(Msg);
结束
Listener:= nil;

FListeners.Clear;
FreeAndNil(FListener);
end; //方法epilogue:为什么有调用IntfClear然后TComponent._Release?

开始
与TBroadcaster.Create做
开始
广播(42);
免费;
结束
结束。

这里是结尾的反汇编:





当天,是IntfClear的两个电话。



那么谁能看到我失踪的明显解释?






更新



嗯,我们马上就来了。 FListeners [i] 需要其结果变量的临时隐式变量。我没有看到,因为我被分配到 Listener ,但当然这是一个不同的变量。



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

  procedure TBroadcaster.Broadcast(Msg:Integer) ; 
var
i:整数;
Intf:IInterface;
侦听器:IListener;
begin
for i:= 0 to FListeners.Count-1 do
begin
Intf:= FListeners [i];
Listener:= Intf as IListener;
Listener.HandleMessage(Msg);
结束
Listener:= nil;

FListeners.Clear;
FreeAndNil(FListener);
结束

以这种方式写入,显然Intf不能被清除,直到结尾。

解决方案

只是一个猜测,但也可能是 FListeners [i] as IListener 使用一个临时变量对于 FListeners [i] 。毕竟这是函数调用的结果。


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

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

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.

Here's the code:

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.

And here's the disassembly of the epilogue:

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

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


UPDATE

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;

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

解决方案

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天全站免登陆