我有两个相同代码的变体:
{$APPTYPE CONSOLE}
uses
System.SysUtils;
type
IMyObject1 = interface
['{4411181F-3531-4D30-AB18-A8326F8C2CD0}']
end;
IMyObject2 = interface
['{41C88E1A-0360-4AC3-B021-125880B23DE5}']
end;
TMyObject = class(TInterfacedObject, IMyObject1, IMyObject2)
public
destructor Destroy; override;
end;
destructor TMyObject.Destroy;
begin
Writeln('Destroy');
inherited;
end;
procedure Variant1;
function GetMyObject: IMyObject2;
var
Obj1: IMyObject1;
begin
Obj1 := TMyObject.Create;
try
Obj1.QueryInterface(IMyObject2, Result);
finally
Obj1 := nil;
end;
end;
var
Obj2: IMyObject2;
begin
Obj2 := GetMyObject;
try
finally
Obj2 := nil;
end;
Writeln('Variant1 end of proc');
end;
function GetMyObject: IMyObject2;
var
Obj1: IMyObject1;
begin
Obj1 := TMyObject.Create;
try
Obj1.QueryInterface(IMyObject2, Result);
finally
Obj1 := nil;
end;
end;
procedure Variant2;
var
Obj2: IMyObject2;
begin
Obj2 := GetMyObject;
try
finally
Obj2 := nil;
end;
Writeln('Variant2 end of proc');
end;
begin
Variant1;
Writeln('---');
Variant2;
Writeln('---');
Readln;
end.
Run Code Online (Sandbox Code Playgroud)
产量
Variant1 end of proc Destroy --- Destroy Variant2 end of proc ---
为什么两种变体的行为不同?
测试环境:Delphi XE7 Windows编译器
在Variant1编译器中决定它需要创建一个隐式局部变量来保存额外的接口引用.它为什么这样做我无法辨别.但是这里发出的代码表明这是发生的事情:
VARIANT1
Project1.dpr.46: Obj2 := GetMyObject; 0041A3BD 55 push ebp 0041A3BE 8D45F8 lea eax,[ebp-$08] 0041A3C1 E836FFFFFF call GetMyObject 0041A3C6 59 pop ecx 0041A3C7 8B55F8 mov edx,[ebp-$08] 0041A3CA 8D45FC lea eax,[ebp-$04] 0041A3CD E816F5FEFF call @IntfCopy // copies into the implicit local
VARIANT2
Project1.dpr.70: Obj2 := GetMyObject; 0041A53B 8D45FC lea eax,[ebp-$04] 0041A53E E839FFFFFF call GetMyObject // no such copy here
隐式本地化在声明它们的函数的最末端完成,就像任何其他局部变量一样.这就是为什么Writeln('Variant1 end of proc')执行,然后最终确定隐式本地,然后释放对该对象的最终引用.
这里非常有趣的一个因素是,如果启用优化,则输出将更改为:
Destroy Variant1 end of proc --- Destroy Variant2 end of proc ---
出于某种原因,编译器决定在启用优化时不创建隐式本地.
当然,以上所有都是针对32位编译器的.64位编译器再次不同,你不知道吗.在64位编译器中,无论优化如何,输出都是按照问题进行的.
我认为这个问题可能与我的问题有关,是否记录了隐式接口变量的编译器处理?据我所知,编译器没有正式的规范或文档,你可以在这里做的最好的事情就是通过观察它的输出来经验地学习.优化改变行为意味着您应该尝试避免易受此类行为更改影响的代码.如果你确实可以预测它们.