最后一次IUnknown.Release调用的时刻

Den*_*mov 8 delphi

我有两个相同代码的变体:

{$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
---

为什么两种变体的行为不同?

Dav*_*nan 5

测试环境: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位编译器中,无论优化如何,输出都是按照问题进行的.

我认为这个问题可能与我的问题有关,是否记录了隐式接口变量的编译器处理?据我所知,编译器没有正式的规范或文档,你可以在这里做的最好的事情就是通过观察它的输出来经验地学习.优化改变行为意味着您应该尝试避免易受此类行为更改影响的代码.如果你确实可以预测它们.