我在StackOverflow上测试了一段代码用于评论,我遇到了一个隐式接口变量引发它的情况.我看不到的是在这种情况下导致它的原因.我有一个迷你工厂,它为新创建的对象返回一个接口.如果我
我用Delphi 10 Seattle测试了这个.是否存在包含隐式接口创建规则的资源,并且是工厂返回的接口不是可靠模式的模式?
program TestRefCount; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, Vcl.Dialogs; type IMyInterface = interface(IInterface) ['{62EB2C46-9B8A-47CE-A881-DB96E6F6437D}'] procedure DoSomething; function GetRefCount: Integer; end; TMyObject = class(TInterfacedObject, IMyInterface) strict private FMyValue: Integer; public procedure Init; procedure DoSomething; function GetRefCount: Integer; end; TMyFactory = class(TObject) private function CreateMyInt: IMyInterface; end; procedure TMyObject.DoSomething; begin MessageDlg(IntToStr(FMyValue), mtInformation, [mbok], 0); end; function TMyObject.GetRefCount: Integer; begin Result := FRefCount; end; procedure TMyObject.Init; begin FMyValue := 100; end; function TMyFactory.CreateMyInt: IMyInterface; var myObject: TMyObject; begin myObject := TMyObject.Create; Assert(myObject.GetRefCount = 0); myObject.Init; Assert(myObject.GetRefCount = 0); Result := myObject; Assert(myObject.GetRefCount = 1); Assert(Result.GetRefCount = 1); end; procedure WorkWithIntf; var myFactory: TMyFactory; myInt: IMyInterface; begin myFactory := TMyFactory.Create; try myInt := myFactory.CreateMyInt; Assert(myInt.GetRefCount = 1); myInt.DoSomething; Assert(myInt.GetRefCount = 1); finally myFactory.Free; end; end; var myFactory: TMyFactory; myInt: IMyInterface; begin try // This case doesn't have an implicit interface variable WorkWithIntf; // This case does have an implicit interface variable myFactory := TMyFactory.Create; try myInt := myFactory.CreateMyInt; Assert(myInt.GetRefCount = 1); // This fails because the refcount is 2 myInt.DoSomething; Assert(myInt.GetRefCount = 1); finally myFactory.Free; end; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end.
这是第一个没有implict接口变量的块:
TestRefCount.dpr.67: myInt := myFactory.CreateMyInt; 005C6A5A 8D55F8 lea edx,[ebp-$08] 005C6A5D 8B45FC mov eax,[ebp-$04] 005C6A60 E83BFEFFFF call TMyFactory.CreateMyInt TestRefCount.dpr.68: Assert(myInt.GetRefCount = 1); 005C6A65 8B45F8 mov eax,[ebp-$08]
这是我们可以看到隐式接口变量的第二个块:
TestRefCount.dpr.86: myInt := myFactory.CreateMyInt; 005CF513 8D55EC lea edx,[ebp-$14] 005CF516 A19CB75D00 mov eax,[$005db79c] 005CF51B E88073FFFF call TMyFactory.CreateMyInt 005CF520 8B55EC mov edx,[ebp-$14] 005CF523 B8A0B75D00 mov eax,$005db7a0 005CF528 E8C7E3E3FF call @IntfCopy TestRefCount.dpr.87: Assert(myInt.GetRefCount = 1); // This fails because the refcount is 2 005CF52D A1A0B75D00 mov eax,[$005db7a0]据我所知,它并没有记录在任何文档中,编译器以不同方式处理本地和全局变量.对于本地人来说,它相信对本地的任务将会成功.因此,不需要隐式本地.这是你的第一个案例.
对于全局变量,编译器更加谨慎.它怀疑地看待全球变量.如果对变量的赋值失败,它会发出防御性代码.编译器首先分配给一个隐式本地,一个肯定会成功的赋值.因此,接口将增加其引用计数.然后它分配给全球.如果失败,则至少隐式本地具有引用,并且能够递减它并正确释放接口.
您可能想知道为什么编译器对分配给您的全局变量感到紧张.你知道它是安全的,不能失败,编译器害怕什么?它的全局变量概念更广泛.它会考虑一个指向接口引用的指针,例如,它是一个全局的.编译器试图防止该指针无效,分配失败,并且没有人引用该接口.编译器只考虑两种情况:本地和全局.它信任赋值给局部变量,而其他所有内容都与可能存在风险的全局变量混为一谈.包括你完全安全的全球.
在我看来,编译器过于谨慎.如果程序员说可以分配变量,我认为编译器不会怀疑它.如果程序员犯了错误,那么程序员肯定应该准备接受后果.它们是否泄漏以及运行时内存访问失败.但设计师采取了一种不同的,更保守的方法.
您看到隐式局部变量的另一种情况是在函数返回值上使用as运算符.例如:
Foo := GetBar as IFoo;
更多内容:The mysterious case of the unexpected implicit interface variable.
那个案子很明显.隐式局部变量是必不可少的,因为提出异常是完全合理的.