Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi InterfacedObject freigeben (https://www.delphipraxis.net/57628-interfacedobject-freigeben.html)

tigerman33 24. Nov 2005 11:39


InterfacedObject freigeben
 
Hallo,

Ich habe Objekte, welche mittels eines Interfaces bestimmte Vorkomnisse an die sie beinhaltende Liste senden.

Im Konstruktor der Liste (das natürlich dieses Interface implementiert) habe ich den Referenzzähler manuell um eins erhöht (damit wenn das letzte Listenelement freigegeben wird nicht automatisch auch die Liste freigegeben wird).
Nun habe ich die Methode BeforeDestruction überschrieben und dort diese eine "zusätzliche" Referenz wieder entfernt, um die Liste ohne Exception freigeben zu können.

Beim _Releasen dieser Referenz wird aber natürlich jetzt wieder der Destruktor aufgerufen, mit allen dazugehörigen Folgen (u.a. auch Aufruf von BeforeDestruction, erneutes _Release etc). Das halte ich für ziemlich unschön. Auf Anhieb sind mir dazu zwei Lösungswege eingefallen:

1. Setzen eines zusätzlichen Boolean-Feldes auf true und dann Abfrage im BeforeDestruction ob nicht gerade schon freigegeben wird (quasi wie das csDestroying von Komponenten)
2. Re-implementieren der _Release-Methode von TInterfacedObject, um damit die "Auto-Zerstörung" bei Referenzzähler=0 zu unterbinden.

Sind aber beides nicht so richtig "schöne" Lösungen, finde ich. Hat noch jemand eine bessere Idee?

Mfg
Christian

BaumWollSchaf 24. Nov 2005 12:37

Re: InterfacedObject freigeben
 
Hallöle. :)

Ein kleines auf das Grundproblem reduziertes Codebeispiel wäre sicher hilfreich.

Grundsätzlich kann man sagen, daß eine manuelle Veränderung des RefCounts so gut wie immer vermieden werden sollte.
Bei Kreuzbezügen kannst du die Interfaceeigenschaft als Pointer definieren und bei Verwendung casten. Auch nicht ganz schön, aber später immer noch übersichtlicher, als die Suche nach dem AddRef im Code. ;)

Böööh!

xaromz 24. Nov 2005 12:54

Re: InterfacedObject freigeben
 
Hallo,

ich habe das bei meinen Objekten folgendermaßen gelöst: Ich habe eine Variable FDestroying eingeführt und setze diese im Destructor. Zusätzlich habe ich _Release wie folgt abgewandelt:
Delphi-Quellcode:
function TInterfacedObject._Release: Integer;
begin
  if FDestroying then
  begin
    Result := FRefCount;
    Exit;
  end;
  Result := InterlockedDecrement(FRefCount);
  if (Result = 0) and not FDestroying then
  begin
    FDestroying := True;
    Destroy;
  end;
end;
Damit bist Du immer auf der sicheren Seite.

Zusätzlich solltest Du gegebenenfalls _AddRef anpassen. Falls Du nämlich in Destroy auf das Objekt zugreifst wird der Zähler wieder erhöht :wall: .
Delphi-Quellcode:
function TInterfacedObject._AddRef: Integer;
begin
  if FDestroying then
    Result := FRefCount
  else
    Result := inherited _AddRef;
end;
Gruß
xaromz

maximov 24. Nov 2005 14:21

Re: InterfacedObject freigeben
 
Wenn ich das richtig sehe besteht das problem nur weil du ein intergefacestes objekt normal als klassen referens speicherst, denn anderenfalls wäre der refCount ja eins und das object würde bleiben, wenn das letzte element aus der liste entfernt würd. Richtig?

Ich mache das immer so, daß ich meiner klasse eine methode Release (die intern _Release aufruft) und free oder destroy garnicht direkt aufrufe, somit stimmt dann der refCount auch für deine klassen referenz.

tigerman33 25. Nov 2005 08:12

Re: InterfacedObject freigeben
 
Danke für eure schnellen Antworten.

@xaromz:
Ja, das mit dem fDestroying hatte ich mir auch überlegt. Ich dachte nur, vielleicht gibt es eine noch etwas elegantere Methode. Werd's aber jetzt wohl einfach so machen.

@maximov:
Sorry, aber ich verstehe nicht so ganz, was du mir sagen willst. Dass ich die Liste von vornherein nur als Interface speichern soll? Bin ich ehrlich gesagt nicht so versessen drauf. Und das mit dem zu niedrigen Referenzzähler hab ich ja umgangen indem ich im Konstruktor einfach die Referenz erhöht hab.

Das Problem ist ja eher folgendes (ich male mal so eine Art "Aufrufdiagramm"):

Liste.Free -> Liste.Destroy -> Liste.BeforeDestruction -> Interface._Release -> Liste.Destroy -> Liste.BeforeDestruction -> Interface._Release
etc etc

Diesen doppelten Aufruf des Destruktors wollte ich umgehen. Aber wie es scheint geht das tatsächlich nur indem ich ein neues Feld einführe.

maximov 5. Dez 2005 09:51

Re: InterfacedObject freigeben
 
Zitat:

Zitat von tigerman33
...
@maximov:
Sorry, aber ich verstehe nicht so ganz, was du mir sagen willst. Dass ich die Liste von vornherein nur als Interface speichern soll? Bin ich ehrlich gesagt nicht so versessen drauf. Und das mit dem zu niedrigen Referenzzähler hab ich ja umgangen indem ich im Konstruktor einfach die Referenz erhöht hab.

Das Problem ist ja eher folgendes (ich male mal so eine Art "Aufrufdiagramm"):

Liste.Free -> Liste.Destroy -> Liste.BeforeDestruction -> Interface._Release -> Liste.Destroy -> Liste.BeforeDestruction -> Interface._Release
etc etc

Diesen doppelten Aufruf des Destruktors wollte ich umgehen. Aber wie es scheint geht das tatsächlich nur indem ich ein neues Feld einführe.

Das ist schon alles richtig, nur das freigeben würde ich nicht direkt über free machen. Ich dachte das eigentlich so: list.Release (free nicht direkt aufrufen) -> List._Release -> RefCount wird 0 -> Liste.Destroy -> fertig!

Das funktioniert dann auch wenn andere interface-referenzen auf die liste zeigen sollten.

tigerman33 5. Dez 2005 11:31

Re: InterfacedObject freigeben
 
Vielen Dank an alle, das Problem hat sich mittlerweile erübrigt. Hab's so gemacht wie von xaromz vorgeschlagen.
Dank an alle die mir geholfen haben. :-D


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:13 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