意外的隐含接口变量的神秘情况 [英] The mysterious case of the unexpected implicit interface variable
问题描述
本质上,它归结为一个隐式接口变量编译器在 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屋!