I asked a similar question about implicit interface variables not so long ago.
The source of this question was an error in my code due to the fact that I did not know about the existence of an implicit interface variable created by the compiler. This variable was completed when the procedure in which it was completed. This, in turn, caused an error because the lifetime of the variable was longer than I expected.
Now I have a simple project to illustrate interesting compiler behavior:
program ImplicitInterfaceLocals; {$APPTYPE CONSOLE} uses Classes; function Create: IInterface; begin Result := TInterfacedObject.Create; end; procedure StoreToLocal; var I: IInterface; begin I := Create; end; procedure StoreViaPointerToLocal; var I: IInterface; P: ^IInterface; begin P := @I; P^ := Create; end; begin StoreToLocal; StoreViaPointerToLocal; end.
StoreToLocal compiles the way you imagine. The local variable I , the result of the function, is passed as an implicit var parameter to Create . StoreToLocal up for StoreToLocal results in a single IntfClear call. There are no surprises.
However, StoreViaPointerToLocal handled differently. The compiler creates an implicit local variable that goes to Create . When Create returns, assignment P^ is performed. This leaves the procedure with two local variables containing links to the interface. StoreViaPointerToLocal up for StoreViaPointerToLocal results in two IntfClear calls.
The compiled code for StoreViaPointerToLocal as follows:
ImplicitInterfaceLocals.dpr.24: begin 00435C50 55 push ebp 00435C51 8BEC mov ebp,esp 00435C53 6A00 push $00 00435C55 6A00 push $00 00435C57 6A00 push $00 00435C59 33C0 xor eax,eax 00435C5B 55 push ebp 00435C5C 689E5C4300 push $00435c9e 00435C61 64FF30 push dword ptr fs:[eax] 00435C64 648920 mov fs:[eax],esp ImplicitInterfaceLocals.dpr.25: P := @I; 00435C67 8D45FC lea eax,[ebp-$04] 00435C6A 8945F8 mov [ebp-$08],eax ImplicitInterfaceLocals.dpr.26: P^ := Create; 00435C6D 8D45F4 lea eax,[ebp-$0c] 00435C70 E873FFFFFF call Create 00435C75 8B55F4 mov edx,[ebp-$0c] 00435C78 8B45F8 mov eax,[ebp-$08] 00435C7B E81032FDFF call @IntfCopy ImplicitInterfaceLocals.dpr.27: end; 00435C80 33C0 xor eax,eax 00435C82 5A pop edx 00435C83 59 pop ecx 00435C84 59 pop ecx 00435C85 648910 mov fs:[eax],edx 00435C88 68A55C4300 push $00435ca5 00435C8D 8D45F4 lea eax,[ebp-$0c] 00435C90 E8E331FDFF call @IntfClear 00435C95 8D45FC lea eax,[ebp-$04] 00435C98 E8DB31FDFF call @IntfClear 00435C9D C3 ret
I can guess why the compiler does this. When this can prove that assigning a result variable does not raise an exception (i.e. if the variable is local), then it directly uses the result variable. Otherwise, it uses an implicit local and copies the interface after the function returns, thereby ensuring that we will not leak the link in case of an exception.
But I can not find any allegations about this in the documentation. This is important because the lifetime of an interface is important, and as a programmer you need to be able to influence it sometimes.
So, does anyone know if there is any documentation on this behavior? If no one knows more about this? How instance fields are handled, I have not tested this yet. Of course, I could try everything for myself, but I am looking for a more formal expression and always prefer not to rely on implementation details developed by trial and error.
Update 1
To answer Remyโs question, itโs important for me when I needed to finish work on the object behind the interface before performing another finalization.
begin AcquirePythonGIL; try PyObject := CreatePythonObject; try //do stuff with PyObject finally Finalize(PyObject); end; finally ReleasePythonGIL; end; end;
As it is written, this is normal. But in real code, I had a second implicit local code that was finalized after the GIL was released and was hacked. I solved the problem by extracting the code inside the Acquire / Release GIL into a separate method and thereby narrowing the scope of the interface variable.