![]() |
Delphi-Version: 10 Berlin
Unstimmigkeit bei Referenzzählung von Interfaces?
Es sieht so aus als würde Delphi eine Referenz doppelt zählen wenn die Zielvariable global ist.
Vermutlich lässt es sich mit den folgenden Beispiel besser beschreiben:
Code:
program Project1;
{$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, Unit1 in 'Unit1.pas'; var IntfA: InterfaceA; TempA : Temp; begin try TempA := Temp.Create; TempA.LocalTest; IntfA := TempA.GetInterfaceA; //_AddRef wird einmal für das Result von GetInterfaceA aufgerufen und einmal für die Zuweisung zu IntfA IntfA := nil; //_Release wird erwartungsgemäß aufgerufen, aber erst beim beenden wird ein weiteres mal _Release aufgerufen, wer hält also die zweite Referenz? Writeln('?'); except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end.
Code:
unit Unit1;
interface type InterfaceA = interface(IInterface) ['{A1229A9D-E207-4CBD-9432-FEFD9D3C1684}'] function Add(sum1, sum2: Integer): Integer; end; ImplA = class(TInterfacedObject, InterfaceA) public function Add(sum1, sum2: Integer): Integer; end; Temp = class public function GetInterfaceA: InterfaceA; procedure LocalTest; end; implementation { Temp } function Temp.GetInterfaceA: InterfaceA; begin Result := ImplA.Create; end; procedure Temp.LocalTest; var IntfA: InterfaceA; begin IntfA := Self.GetInterfaceA; //_AddRef wird hier jedoch nur ein einziges mal aufgerufen, woher kommt der Unterschied? IntfA := nil; //_Release wird erwartungsgemäß aufgerufen end; { ImplA } function ImplA.Add(sum1, sum2: Integer): Integer; begin Result := 42; end; end. |
AW: Unstimmigkeit bei Referenzzählung von Interfaces?
Bei der Rückgabe eines Interfaces aus einer Funktion macht Delphi folgendes - Pseudocode:
Delphi-Quellcode:
Die vom Compiler implizit erzeugte Variable wird erst am Ende des jeweiligen Scopes finalisiert.
var
meineVariable: IInterface; compilerTempVariable: IInterface; begin compilerTempVariable := MeineFunktion; meineVariable := compilerTempVariable; ... end Hier ein Auszug aus dem Disassembly deiner Anwendung:
Code:
Packen wir den gesamten Code in eine Routine, sieht der Code an dieser Stelle wie folgt aus (wie z.B. in deiner LocalTest Methode):
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
Code:
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.
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 Das kann man z.B. sehen, wenn du einfach mal zweimal hintereinander
Delphi-Quellcode:
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.
IntfA := TempA.GetInterfaceA;
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
Delphi-Quellcode:
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.
IntfA := TempA.GetInterfaceA;
Was der Compiler im Übrigen nicht absichert, ist folgendes:
Delphi-Quellcode:
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.
try
IntfA := TempA.GetInterfaceA; except end; IntfA := TempA.GetInterfaceA; |
AW: Unstimmigkeit bei Referenzzählung von Interfaces?
Der Effekt wird auch hier bei StackOverflow beschrieben:
![]() |
AW: Unstimmigkeit bei Referenzzählung von Interfaces?
@Stevie
Danke für die ausführliche Erklärung! Auch wenn ich Delphi nicht mag, die Community ist spitze :thumb: |
Alle Zeitangaben in WEZ +1. Es ist jetzt 21:46 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz