Bei der Rückgabe eines Interfaces aus einer Funktion macht Delphi folgendes - Pseudocode:
Delphi-Quellcode:
var
meineVariable: IInterface;
compilerTempVariable: IInterface;
begin
compilerTempVariable := MeineFunktion;
meineVariable := compilerTempVariable;
...
end
Die vom Compiler implizit erzeugte Variable wird erst am Ende des jeweiligen Scopes finalisiert.
Hier ein Auszug aus dem Disassembly deiner Anwendung:
Code:
Project1.dpr.17: IntfA := TempA.GetInterfaceA; //_AddRef wird einmal für das Result von GetInterfaceA aufgerufen und einmal für die Zuweisung zu IntfA
0041D596 8D55EC lea edx,[ebp-$14] // <- temp Variable
0041D599 A1BC584200 mov eax,[$004258bc]
0041D59E E8C5D8FFFF call Temp.GetInterfaceA
0041D5A3 8B55EC mov edx,[ebp-$14]
0041D5A6 B8B8584200 mov eax,$004258b8
0041D5AB E870CEFEFF call @IntfCopy // <- zuweisen von temp Variable auf IntfA
Packen wir den gesamten Code in eine Routine, sieht der Code an dieser Stelle wie folgt aus (wie z.B. in deiner LocalTest Methode):
Code:
Project1.dpr.18: IntfA := TempA.GetInterfaceA; //_AddRef wird einmal für das Result von GetInterfaceA aufgerufen und einmal für die Zuweisung zu IntfA
0041AF4C 8D55FC lea edx,[ebp-$04] // <- IntfA
0041AF4F 8B45F8 mov eax,[ebp-$08] // <- TempA
0041AF52 E811FFFFFF call Temp.GetInterfaceA
Warum ist das so? Dazu muss man folgendes wissen: Bei einem Result vom Typ Interface (und anderen gemanageten Typen auch) wird der Methode das Result als var Parameter übergeben.
Das kann man z.B. sehen, wenn du einfach mal zweimal hintereinander
IntfA := TempA.GetInterfaceA;
aufrufst und einen Stoppunkt in GetInterfaceA setzt, dann siehst du das beim zweiten Aufruf in Result schon was drin steht, nämlich der Wert aus deim ersten Aufruf.
Und weil das so ist, sichert der Compiler diesen Code gegen mögliche Exceptions ab. Wenn nämlich nach der Zuweisung auf Result eine
Exception in GetInterfaceA auftreten würde, wäre trotzdem die Result zuweisung schon passiert, was aber nicht sein darf, weil die Funktion ja nicht ordnungsgemäß zurück kommt und demnach die Zuweisung von
IntfA := TempA.GetInterfaceA;
nicht passieren wird. Handelt es sich hier aber um eine lokale Variable, kann der Compiler auf diese Absicherung über die temporäre Variable verzichten, da niemand anders außerhalb dieser Routine auf diesen Wert zugreifen kann - was bei einer globalen Variable anders sein kann.
Was der Compiler im Übrigen nicht absichert, ist folgendes:
Delphi-Quellcode:
try
IntfA := TempA.GetInterfaceA;
except
end;
IntfA := TempA.GetInterfaceA;
Angenommen beim ersten Aufruf von GetInterfaceA kommt nach dem Result zuweisen noch eine
Exception. IntfA ist nun trotzdem nicht mehr nil, weil das passiert, was ich oben beschrieben habe.