Ich habe vor nicht allzu langer Zeit eine ähnliche Frage zu impliziten Schnittstellenvariablen gestellt.
Die Quelle dieser Frage war ein Fehler in meinem Code, weil ich nicht wusste, dass eine implizite Schnittstellenvariable vorhanden ist, die vom Compiler erstellt wurde. Diese Variable wurde abgeschlossen, als die Prozedur, der sie gehörte, abgeschlossen war. Dies verursachte wiederum einen Fehler, da die Lebensdauer der Variablen länger war als erwartet.
Jetzt habe ich ein einfaches Projekt, um ein interessantes Verhalten des Compilers zu veranschaulichen:
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
wird so zusammengestellt, wie Sie es sich vorstellen können. Die lokale Variable I
, das Ergebnis der Funktion, wird als impliziter var
Parameter an übergeben Create
. Das Aufräumen StoreToLocal
führt zu einem einzigen Anruf bei IntfClear
. Keine Überraschungen da.
Jedoch StoreViaPointerToLocal
wird anders behandelt. Der Compiler erstellt eine implizite lokale Variable, an die er übergeben wird Create
. Bei der Create
Rückkehr wird die Zuordnung zu P^
ausgeführt. Dadurch bleiben der Routine zwei lokale Variablen übrig, die Verweise auf die Schnittstelle enthalten. Das Aufräumen StoreViaPointerToLocal
führt zu zwei Anrufen bei IntfClear
.
Der kompilierte Code für StoreViaPointerToLocal
lautet wie folgt:
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
Ich kann mir vorstellen, warum der Compiler dies tut. Wenn nachgewiesen werden kann, dass die Zuweisung zur Ergebnisvariablen keine Ausnahme auslöst (dh wenn die Variable lokal ist), wird die Ergebnisvariable direkt verwendet. Andernfalls wird ein implizites lokales Element verwendet und die Schnittstelle kopiert, sobald die Funktion zurückgegeben wurde. Auf diese Weise wird sichergestellt, dass die Referenz im Falle einer Ausnahme nicht verloren geht.
Aber ich kann keine Aussage dazu in der Dokumentation finden. Dies ist wichtig, da die Lebensdauer der Benutzeroberfläche wichtig ist und Sie als Programmierer gelegentlich Einfluss darauf nehmen müssen.
Weiß jemand, ob es eine Dokumentation dieses Verhaltens gibt? Wenn nicht, hat jemand mehr Wissen darüber? Wie mit Instanzfeldern umgegangen wird, habe ich noch nicht überprüft. Natürlich könnte ich alles selbst ausprobieren, aber ich suche nach einer formelleren Aussage und ziehe es immer vor, mich nicht auf Implementierungsdetails zu verlassen, die durch Ausprobieren ausgearbeitet wurden.
Update 1
Um Remys Frage zu beantworten, war es mir wichtig, wann ich das Objekt hinter der Schnittstelle finalisieren musste, bevor ich eine weitere Finalisierung durchführte.
begin
AcquirePythonGIL;
try
PyObject := CreatePythonObject;
try
//do stuff with PyObject
finally
Finalize(PyObject);
end;
finally
ReleasePythonGIL;
end;
end;
Wie so geschrieben ist es in Ordnung. Aber im realen Code hatte ich einen zweiten impliziten lokalen, der abgeschlossen wurde, nachdem die GIL veröffentlicht und bombardiert wurde. Ich habe das Problem gelöst, indem ich den Code in der Acquire / Release GIL in eine separate Methode extrahiert und damit den Umfang der Schnittstellenvariablen eingeschränkt habe.
procedure StoreViaAbsoluteToLocal; var I: IInterface; I2: IInterface absolute I; begin I2 := Create; end;