Einzelnen Beitrag anzeigen

Benutzerbild von Stevie
Stevie

Registriert seit: 12. Aug 2003
Ort: Soest
4.027 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#2

AW: Unstimmigkeit bei Referenzzählung von Interfaces?

  Alt 14. Mär 2018, 14:25
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.
Stefan
“Simplicity, carried to the extreme, becomes elegance.” Jon Franklin

Delphi Sorcery - DSharp - Spring4D - TestInsight

Geändert von Stevie (14. Mär 2018 um 14:42 Uhr)
  Mit Zitat antworten Zitat