意外隐式接口变量的神秘案例 [英] The mysterious case of the unexpected implicit interface variable
问题描述
我最近遇到了一些我无法解释也无法解释的行为,这些行为与 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屋!