我最近遇到了一些我无法解释的行为,与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无法在结尾之前被清除.