Hice una pregunta similar sobre las variables de interfaz implícitas no hace mucho tiempo.
La fuente de esta pregunta fue un error en mi código debido a que no estaba al tanto de la existencia de una variable de interfaz implícita creada por el compilador. Esta variable se finalizó cuando finalizó el trámite que la poseía. Esto, a su vez, provocó un error debido a que la vida útil de la variable era más larga de lo que había anticipado.
Ahora, tengo un proyecto simple para ilustrar un comportamiento interesante del 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
se compila tal como se imagina. La variable local I
, el resultado de la función, se pasa como var
parámetro implícito a Create
. El orden para StoreToLocal
resultados en una sola llamada a IntfClear
. No hay sorpresas ahí.
Sin embargo, StoreViaPointerToLocal
se trata de manera diferente. El compilador crea una variable local implícita a la que pasa Create
. Cuando Create
regresa, se realiza la asignación a P^
. Esto deja la rutina con dos variables locales que contienen referencias a la interfaz. El tidy up for StoreViaPointerToLocal
da como resultado dos llamadas a IntfClear
.
El código compilado para StoreViaPointerToLocal
es así:
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
Puedo adivinar por qué el compilador está haciendo esto. Cuando puede probar que la asignación a la variable de resultado no generará una excepción (es decir, si la variable es local), entonces usa la variable de resultado directamente. De lo contrario, usa un local implícito y copia la interfaz una vez que la función ha regresado, lo que garantiza que no filtremos la referencia en caso de una excepción.
Pero no puedo encontrar ninguna declaración de esto en la documentación. Es importante porque la vida útil de la interfaz es importante y, como programador, debe poder influir en ella en ocasiones.
Entonces, ¿alguien sabe si hay alguna documentación de este comportamiento? Si no, ¿alguien tiene más conocimiento al respecto? ¿Cómo se manejan los campos de instancia? Aún no lo he verificado. Por supuesto, podría probarlo todo por mí mismo, pero estoy buscando una declaración más formal y siempre prefiero evitar confiar en los detalles de implementación resueltos por prueba y error.
Actualización 1
Para responder a la pregunta de Remy, me importaba cuándo necesitaba finalizar el objeto detrás de la interfaz antes de realizar otra finalización.
begin
AcquirePythonGIL;
try
PyObject := CreatePythonObject;
try
//do stuff with PyObject
finally
Finalize(PyObject);
end;
finally
ReleasePythonGIL;
end;
end;
Como está escrito así, está bien. Pero en el código real tenía un segundo local implícito que se finalizó después de que se lanzó el GIL y se bombardeó. Resolví el problema extrayendo el código dentro de Adquirir / Liberar GIL en un método separado y así estreché el alcance de la variable de interfaz.