I have two variants of the same code:
{$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.
Output
Variant1 end of proc Destroy --- Destroy Variant2 end of proc ---
Why is the behaviour of the two variants different?
Test environment: Delphi XE7 Windows compilers
In Variant1
the compiler decides that it needs to create an implicit local variable to hold an extra interface reference. Quite why it does so I cannot discern. But here's the emitted code to show that this is what happens:
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
Implicit locals are finalized at the very end of the function in which they are declared, just like any other local variable. Which is why Writeln('Variant1 end of proc')
executes, and then the implicit local is finalized, and then the final reference to the object is released.
One factor that is quite interesting here is that if you enable optimization then the output changes to:
Destroy Variant1 end of proc --- Destroy Variant2 end of proc ---
For some reason the compiler decides not to create an implicit local when optimization is enabled.
Of course, all the above is for the 32 bit compiler. The 64 bit compiler is different again, wouldn't you know it. In the 64 bit compiler the output is as per the question irrespective of optimization.
I think that this issue is perhaps related to my question Is the compiler treatment of implicit interface variables documented? To the very best of my knowledge, there is no official specification or documentation of what the compiler does, and the best you can do here is learn empirically by observing its output. That optimization changes behaviour means that you should be trying to avoid code that is susceptible to such behavioural changes. If indeed you can predict them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With