AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Object-Pascal / Delphi-Language Delphi Unstimmigkeit bei Referenzzählung von Interfaces?
Thema durchsuchen
Ansicht
Themen-Optionen

Unstimmigkeit bei Referenzzählung von Interfaces?

Ein Thema von Elrond · begonnen am 14. Mär 2018 · letzter Beitrag vom 14. Mär 2018
Antwort Antwort
Elrond

Registriert seit: 29. Sep 2014
71 Beiträge
 
#1

Unstimmigkeit bei Referenzzählung von Interfaces?

  Alt 14. Mär 2018, 13:08
Delphi-Version: 10 Berlin
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.
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

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

AW: Unstimmigkeit bei Referenzzählung von Interfaces?

  Alt 14. Mär 2018, 13: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 13:42 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
10.993 Beiträge
 
Delphi 12 Athens
 
#3

AW: Unstimmigkeit bei Referenzzählung von Interfaces?

  Alt 14. Mär 2018, 13:34
Der Effekt wird auch hier bei StackOverflow beschrieben: The mysterious case of the unexpected implicit interface variable
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Elrond

Registriert seit: 29. Sep 2014
71 Beiträge
 
#4

AW: Unstimmigkeit bei Referenzzählung von Interfaces?

  Alt 14. Mär 2018, 13:49
@Stevie

Danke für die ausführliche Erklärung!
Auch wenn ich Delphi nicht mag, die Community ist spitze
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:24 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz