Eu fiz uma pergunta semelhante sobre variáveis de interface implícitas não há muito tempo.
A origem desta pergunta foi um bug em meu código devido ao fato de eu não estar ciente da existência de uma variável de interface implícita criada pelo compilador. Esta variável foi finalizada quando o procedimento que a possuía terminou. Isso, por sua vez, causou um bug devido ao tempo de vida da variável ser maior do que eu esperava.
Agora, tenho um projeto simples para ilustrar alguns comportamentos interessantes do compilador:
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
é compilado exatamente como você imagina. A variável local I
, o resultado da função, é passada como um var
parâmetro implícito para Create
. A organização dos StoreToLocal
resultados em uma única chamada para IntfClear
. Sem surpresas aí.
No entanto, StoreViaPointerToLocal
é tratado de forma diferente. O compilador cria uma variável local implícita para a qual ele passa Create
. Quando Create
retorna, a atribuição a P^
é realizada. Isso deixa a rotina com duas variáveis locais contendo referências à interface. A arrumação para StoreViaPointerToLocal
resultados em duas chamadas para IntfClear
.
O código compilado para StoreViaPointerToLocal
é assim:
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
Posso imaginar por que o compilador está fazendo isso. Quando puder provar que a atribuição à variável de resultado não levantará uma exceção (ou seja, se a variável for local), ele usará a variável de resultado diretamente. Caso contrário, ele usa um local implícito e copia a interface assim que a função retorna, garantindo assim que não vazemos a referência em caso de uma exceção.
Mas não consigo encontrar nenhuma declaração sobre isso na documentação. É importante porque o tempo de vida da interface é importante e, como programador, você precisa ser capaz de influenciá-lo ocasionalmente.
Então, alguém sabe se existe alguma documentação desse comportamento? Se não, alguém tem mais conhecimento sobre isso? Como são tratados os campos de instância, ainda não verifiquei. Claro que eu poderia experimentar tudo sozinho, mas estou procurando uma declaração mais formal e sempre prefiro evitar depender de detalhes de implementação elaborados por tentativa e erro.
Atualização 1
Para responder à pergunta de Remy, foi importante para mim quando precisei finalizar o objeto por trás da interface antes de realizar outra finalização.
begin
AcquirePythonGIL;
try
PyObject := CreatePythonObject;
try
//do stuff with PyObject
finally
Finalize(PyObject);
end;
finally
ReleasePythonGIL;
end;
end;
Como está escrito assim, está tudo bem. Mas no código real eu tinha um segundo local implícito que foi finalizado depois que o GIL foi lançado e explodiu. Resolvi o problema extraindo o código dentro do Acquire / Release GIL em um método separado e, assim, estreitei o escopo da variável de interface.