Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi RefCount nach Erstellung von TInterfacedObject gleich 0 (https://www.delphipraxis.net/165738-refcount-nach-erstellung-von-tinterfacedobject-gleich-0-a.html)

s.h.a.r.k 13. Jan 2012 14:12

Delphi-Version: XE2

RefCount nach Erstellung von TInterfacedObject gleich 0
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo zusammen,

ich habe gerade ein irgendwie unschönes Problem mit Interfaces. Gehe um Moment immer mehr dazu über Interfaces zu verwenden, da es an einigen Stellen echt praktisch sein kann. Dann gibt es aber immer wieder merkwürdige Dinge, die ich (noch) nicht so recht verstehe... Und zwar geht um im Moment um den folgenden Code -- ja, der ist leicht anders als der auf dem Bild im Anhang!
Delphi-Quellcode:
TBaseStartupProcess = class(TInterfacedObject, IStartupProcess, IStartupProcessCommandContainer)
end;

class function TStartupManagerFactory.Create(): IStartupProcess;
var
  StartupProcess : TBaseStartupProcess;
begin
  StartupProcess := TBaseStartupProcess.Create();
  AddStartupCommands(StartupProcess);
  Result := StartupProcess;
end;
Ich finde, dass da ja nichts besonderes dran ist. Mit der lokalen Variablen muss ich arbeiten, da die AddStartupCommands einen Parameter vom Typ TBaseStartupProcess erwartet -- das Interface kann ich hier nicht nutzen, da ich in der AddStartupCommands Dinge mit dem Objekt mache, die nicht vom Interface abgedeckt sind.

Jedenfallst ist es so, dass wenn ich obigen Code verwende, direkt nach der AddStartupCommands-Methode, die Destroy-Methode von StartupProcess aufgerufen wird. Habe das dann weiter verfolgt und bin darauf gestoßen, dass direkt nach dem Erzeugen des Objekts RefCount gleich 0 ist. Hier die Frage: wie kann sowas sein? StartupProcess zeigt doch auf das neu erzeugte Objekt, ergo müsste doch RefCount gleich 1 sein -- im Screenshot könnt ihr das auch nochmals sehen.

Wenn ich den Code umstelle, wie im Screenshot gezeigt, so ist alles in Butter und die Destroy-Methode wird nach dem Aufruf von AddStartupCommands nicht aufgerufen, da RefCount nach der Zuweisung
Delphi-Quellcode:
Result := StartupProcess;
1 ist, und nicht 0.

Ich hoffe, dass hier jemand Licht ins Dunkel bringen kann!

Sir Rufo 13. Jan 2012 15:05

AW: RefCount nach Erstellung von TInterfacedObject gleich 0
 
Die Referenzzählung erfolgt IMHO erst dann, wenn die Instanz zu einer Interface-Variablen zugeordnet wird.

Probier es mal so
Delphi-Quellcode:
class function TStartupManagerFactory.Create(): IStartupProcess;
var
  StartupProcess : TBaseStartupProcess;
begin
  StartupProcess := TBaseStartupProcess.Create();
  Result := StartupProcess;
  AddStartupCommands(StartupProcess); // Hier mal den Haltepunkt setzen und prüfen
end;

ASM 13. Jan 2012 15:06

AW: RefCount nach Erstellung von TInterfacedObject gleich 0
 
Zitat:

Zitat von s.h.a.r.k (Beitrag 1145795)
... bin darauf gestoßen, dass direkt nach dem Erzeugen des Objekts RefCount gleich 0 ist. Hier die Frage: wie kann sowas sein? StartupProcess zeigt doch auf das neu erzeugte Objekt, ergo müsste doch RefCount gleich 1 sein...

Ich hoffe, dass hier jemand Licht ins Dunkel bringen kann!


Möglicherweise hiermit:
Die Erklärung dazu könnte durch ein Problem verursacht sein, das detailliert hier beschrieben worden ist:
Kosch, Andreas: "COM/DCOM/COM+ mit Delphi" (ISBN 3-935042-01-9), p.92-94.

Kurz zusammengefasst: Es liegt womöglich an der Verwendung der "normalen" Delphi-Variablen StartupProcess: TBaseStartupProcess für den Zugriff auf das interfaced Objekt innerhalb der function TStartupManagerFactory.Create() anstatt, wie es richtiger wäre, der Verwendung unmittelbar einer echten Interface-Variablen (StartupProcess : IDingsbums) für das COM-Objekt. Details siehe genannte Referenz.
Von Kosch vorgeschlagener Workaround: den Verwendungszähler des COM-Objekts manuell durch Aufruf von _Addref um 1 hochzählen.

Blup 13. Jan 2012 15:11

AW: RefCount nach Erstellung von TInterfacedObject gleich 0
 
RefCount sagt nicht aus wie oft das Objekt verwendet wird, sondern wie viele Interface-Variablen existieren, die auf dieses Objekt verweisen. Deshalb ist RefCount nach dem Erzeugen erst einmal 0. Das Objekt wird nur automatisch freigegeben, wenn RefCount von 1 auf 0 fällt.

Arbeitet AddStartupCommands() durchgehend nur mit dem Objekt, so ist alles in Ordnung.
Aber vermutlich wird das Objekt dort auch einer Interfacevariable zugewiesen, die nur temporär existiert. Dadurch steigt RefCount und fällt wieder auf 0, mit dem bekannten Ergebnis.

Zitat:

Mit der lokalen Variablen muss ich arbeiten, da die AddStartupCommands einen Parameter vom Typ TBaseStartupProcess erwartet -- das Interface kann ich hier nicht nutzen, da ich in der AddStartupCommands Dinge mit dem Objekt mache, die nicht vom Interface abgedeckt sind.
Gegen die lokale Variable ist im Prinzip nichts einzuwenden, wenn diese für die Initialisierung des Objektes notwendig ist. Sobald aber ein Interface erzeugt ist, sollte man nur noch damit arbeiten. Wenn AddStartupCommands Dinge von dem Objekt erwartet, die durch die vorhandenen Interfaces nicht abgedeckt werden, so könnte man einfach ein weiteres Interface hinzufügen.

Die Variante von Sir Rufo funktioniert auch.
Allerdings sollte man solche Kniffe ausführlich kommentieren.
Sonst wundert man sich, wenn man an dieser Stelle z.B. in zwei Jahren nur eine Kleinigkeit ändert, warum nichts mehr geht...

himitsu 13. Jan 2012 15:21

AW: RefCount nach Erstellung von TInterfacedObject gleich 0
 
Wenn mit Interfaces gearbeitet wird, dann sollte dieses durchgängig gemacht werden.
Delphi-Quellcode:
class function TStartupManagerFactory.Create(): IStartupProcess;
begin
  Result := TBaseStartupProcess.Create();
  Result := StartupProcess;
  AddStartupCommands(Result);
end;
Standardmäßig ist eine Mischbehandlung nicht sicher, da die Referenzzählung keine Ahnung davon hat, ob irgendwo Variablen Referenzen auf das Objekt besitzen, da nur Referenzen auf das Interface gezählt werden.

Wie ist denn AddStartupCommands deklariert?
(der Parameter als Interface oder als Objekt)

mjustin 13. Jan 2012 15:37

AW: RefCount nach Erstellung von TInterfacedObject gleich 0
 
Zitat:

Zitat von himitsu (Beitrag 1145818)
Wenn mit Interfaces gearbeitet wird, dann sollte dieses durchgängig gemacht werden.

Natürlich, aber irgendwo muss halt doch eine implementierende Klasse instanziiert werden.
Wo kann etwas schiefgehen, wenn man wie im Beispiel der Factoryklasse, die erzeugte Instanz temporär in einer lokalen Variable speichert?

Uwe Raabe 13. Jan 2012 16:00

AW: RefCount nach Erstellung von TInterfacedObject gleich 0
 
Wenn du dir mal die Implementation von TInterfacedObject ansiehst, kannst du sehen, wie in NewInstance der RefCount auf 1 gesetzt wird und in AfterConstruction wieder um 1 verringert wird. Wenn also nicht innerhalb des Create ein implizites _AddRef erfolgt, ist der RefCount nach dem Create wieder 0. Das muss auch so sein, da er sonst ja nie mehr auf 0 kommen könnte und die Instanz nicht mehr freigegeben würde. Die Zuweisung der Instanz auf eine simple Variable dieser Klasse oder einer Oberklasse löst übrigens keine implizite Referenzzählung aus (wie Sir Rufo richtig bemerkt hat), dies geht nur bei Interface-Variablen und -Parametern.

Das erste was also passieren muss, ist die Instanz einer Interface-Variablen (z.B. dem result) zuzuweisen, damit der RefCount erhöht wird. Vorher kannst du mit der Instanz eigentlich nur sehr wenig anfangen. Auf gar keinen Fall darf dabei ein _AddRef/_Release aufgerufen werden (was offenbar irgendwo innerhalb der Methode AddStartupCommands passiert), da sonst die Instanz sofort freigegeben wird. Deine lokale Instanzvariable zeigt dann eben ins Nirwana.

Diese Krücke mit dem künstliche hochzählen während des Create macht man übrigens eben deshalb, damit solche impliziten Interface-Zuweisungen innerhalb der Create-Sequenz nicht gleich zur Zerstörung der Instanz führen. Inkonsequenterweise hat man dies für die Destroy-Sequenz wohl vergessen.

Sir Rufo 13. Jan 2012 16:11

AW: RefCount nach Erstellung von TInterfacedObject gleich 0
 
So solte das doch eigentlich funktionieren
Delphi-Quellcode:
class function TStartupManagerFactory.Create(): IStartupProcess;
begin
  Result := TBaseStartupProcess.Create();
  AddStartupCommands( TBaseStartupProcess( Result ) );
end;

himitsu 13. Jan 2012 16:48

AW: RefCount nach Erstellung von TInterfacedObject gleich 0
 
Zitat:

Zitat von Sir Rufo (Beitrag 1145830)
So solte das doch eigentlich funktionieren

Wenn hier nicht zufällig ein bissl Compilermagic mitspielt, dann definitiv NEIN.

Ein Interface-Zeiger ist kein Objekt-Zeiger, also kann man das nicht einfach so billig umcasten.

Zitat:

Zitat von mjustin
Natürlich, aber irgendwo muss halt doch eine implementierende Klasse instanziiert werden.
Wo kann etwas schiefgehen, wenn man wie im Beispiel der Factoryklasse, die erzeugte Instanz temporär in einer lokalen Variable speichert?

Möglichst direkt in die Interfacevariable reinerstellen.

Sobald man auch nur eine Interfacevariable erstellt ht, ist die Zuferlässigkeit von Objektzeigern nicht mehr gegeben, da das Objekt vorzeitig/ungewollt über die Interface-Referenzzählung freigegeben werden könnte.

Aus diesem Grund hatte ich vor eine Weile angefangen eine Hybride zu erstellen, welche die Interfacereferenzen und Objektreferenzen gleichermaßen beachtet.
Leider ist das in Delphi (speziell die Erstellung des Objektes) nicht komplett und sicher umsetzbar,
da man nicht automatisch erkennen kann, ob das erstellte Objekt, bei
Delphi-Quellcode:
x := TIrgendwas.Create;
direkt in eine Interfacevariable geht oder ob das eine Objektvariable ist. (also um was es sich beim X handelt)

Sir Rufo 13. Jan 2012 16:59

AW: RefCount nach Erstellung von TInterfacedObject gleich 0
 
Zitat:

Zitat von himitsu (Beitrag 1145836)
Zitat:

Zitat von Sir Rufo (Beitrag 1145830)
So solte das doch eigentlich funktionieren

Wenn hier nicht zufällig ein bissl Compilermagic mitspielt, dann definitiv NEIN.

Ein Interface-Zeiger ist kein Objekt-Zeiger, also kann man das nicht einfach so billig umcasten.

Oh, dann habe ich wohl das Hummel-Syndrom ... denn das wusste ich nicht.
Aus diesem Grunde mache ich das so ... und es funktioniert :mrgreen:

Also, das sollte nicht nur, sondern das Casten funktioniert genau so.

Eine Hummel kann eigentlich gar nicht fliegen, da die Flügel viel zu klein für ihr Gewicht sind.
Da die Hummel das nicht weißt, fliegt sie einfach.


Alle Zeitangaben in WEZ +1. Es ist jetzt 20:05 Uhr.
Seite 1 von 2  1 2      

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