AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Object-Pascal / Delphi-Language Delphi Kreuzreferenzen - selbsthaltende Interfaces
Thema durchsuchen
Ansicht
Themen-Optionen

Kreuzreferenzen - selbsthaltende Interfaces

Ein Thema von himitsu · begonnen am 25. Apr 2010 · letzter Beitrag vom 25. Apr 2010
Antwort Antwort
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.123 Beiträge
 
Delphi 12 Athens
 
#1

Kreuzreferenzen - selbsthaltende Interfaces

  Alt 25. Apr 2010, 07:44
Einige kennen doch bestimmt das Problem, daß wenn sich Interfaces gegenseitig referenzieren,
daß diese, selbst bei Freigabe aller externen Referenzen, sich dennoch nicht freigeben, da sie sich ja gegenseitig halten.
Delphi-Quellcode:
type
  TObjektMaster = Class(InterfacedObject);
    FSlave: IInterface;
  End;

  TObjektSlave = Class(InterfacedObject);
    FMaster: IInterface;
  End;
Nja, da ich hiermit immer mal wieder Probleme hatte und deswegen oftmals sogar auf Interfaces verzichtete,
wollte ich endlich mal dafür eine befriedigende Lösung finden.


Erstmal 2 "einfache" Varianten, bei welchen man Referenzen registrieren kann, welche praktisch nicht gezählt werden sollen.
Delphi-Quellcode:
type
  IXRefInterface = Interface
    // register after set to the reference variable
    // unregister before free the reference variable
    Procedure RegisterXRef;
    Function UnregisterXRef: Boolean; // FALSE if the object was destroyed
  End;
  TXRefInterfacedObject = Class(TObject, IInterface, IXRefInterface)
  Protected
    {IInterface}
    Function QueryInterface(Const IID: TGUID; Out Obj): HResult; StdCall;
    Function _AddRef: Integer; StdCall;
    Function _Release: Integer; StdCall;
  Protected
    {IXRefInterface}
    FRefCount, FXRefs: Integer;
    Procedure RegisterXRef;
    Function UnregisterXRef: Boolean; // FALSE if the object was destroyed
    Procedure DoFreeXRefs; Virtual;
  Public
    Procedure AfterConstruction; Override;
    Procedure BeforeDestruction; Override;
    Class Function NewInstance: TObject; Override;
    Property RefCount: Integer Read FRefCount;
  End;

Function TXRefInterfacedObject.QueryInterface(Const IID: TGUID; Out Obj): HResult;
  Begin
    If GetInterface(IID, Obj) Then Result := S_OK
    Else Result := E_NOINTERFACE;
  End;

Function TXRefInterfacedObject._AddRef: Integer;
  Begin
    Result := InterlockedIncrement(FRefCount);
  End;

Function TXRefInterfacedObject._Release: Integer;
  Begin
    Result := InterlockedDecrement(FRefCount);
    If Result = FXRefs Then Destroy;
  End;

Procedure TXRefInterfacedObject.RegisterXRef;
  Begin
    InterlockedIncrement(FXRefs);
  End;

Function TXRefInterfacedObject.UnregisterXRef: Boolean;
  Begin
    InterlockedDecrement(FXRefs);
    Result := FRefCount <> FXRefs;
    If not Result Then Destroy;
  End;

Procedure TXRefInterfacedObject.DoFreeXRefs;
  Begin
    // do nothing
  End;

Procedure TXRefInterfacedObject.AfterConstruction;
  Begin
    // release the constructor's implicit refcount
    InterlockedDecrement(FRefCount);
  End;

Procedure TXRefInterfacedObject.BeforeDestruction;
  Begin
    If FRefCount <> FXRefs Then Error(reInvalidPtr);
    DoFreeXRefs;
    If FRefCount <> 0 Then Error(reInvalidPtr);
  End;

Class Function TXRefInterfacedObject.NewInstance: TObject;
  Begin
    Result := Inherited NewInstance;
    TInterfacedObject(Result).FRefCount := 1;
  End;
Delphi-Quellcode:
type
  IXRefInterface = Interface
    // release before free the reference variable
    Function GetNonCounteredReference: IInterface;
    Procedure ReleaseNonCounteredReference;
  End;
  TXRefInterfacedObject = Class(TObject, IInterface, IXRefInterface)
  Protected
    {IInterface}
    Function QueryInterface(Const IID: TGUID; Out Obj): HResult; StdCall;
    Function _AddRef: Integer; StdCall;
    Function _Release: Integer; StdCall;
  Protected
    {IXRefInterface}
    FRefCount, FXRefs: Integer;
    Function GetNonCounteredReference: IInterface;
    Procedure ReleaseNonCounteredReference;
    Procedure DoFreeXRefs; Virtual;
  Public
    Procedure AfterConstruction; Override;
    Procedure BeforeDestruction; Override;
    Class Function NewInstance: TObject; Override;
    Property RefCount: Integer Read FRefCount;
  End;

Function TXRefInterfacedObject.QueryInterface(Const IID: TGUID; Out Obj): HResult;
  Begin
    If GetInterface(IID, Obj) Then Result := S_OK
    Else Result := E_NOINTERFACE;
  End;

Function TXRefInterfacedObject._AddRef: Integer;
  Begin
    Result := InterlockedIncrement(FRefCount);
  End;

Function TXRefInterfacedObject._Release: Integer;
  Begin
    Result := InterlockedDecrement(FRefCount);
    If Result = FXRefs Then Destroy;
  End;

Function GetNonCounteredReference: IInterface;
  Begin
    InterlockedIncrement(FXRefs);
    Result := Self;
  End;

Procedure ReleaseNonCounteredReference;
  Begin
    InterlockedDecrement(FXRefs);
    //If FRefCount = FXRefs Then Destroy;
  End;

Procedure TXRefInterfacedObject.DoFreeXRefs;
  Begin
    // do nothing
  End;

Procedure TXRefInterfacedObject.AfterConstruction;
  Begin
    // release the constructor's implicit refcount
    InterlockedDecrement(FRefCount);
  End;

Procedure TXRefInterfacedObject.BeforeDestruction;
  Begin
    If FRefCount <> FXRefs Then Error(reInvalidPtr);
    DoFreeXRefs;
    If FRefCount <> 0 Then Error(reInvalidPtr);
  End;

Class Function TXRefInterfacedObject.NewInstance: TObject;
  Begin
    Result := Inherited NewInstance;
    TInterfacedObject(Result).FRefCount := 1;
  End;

Und dann noch eine Variante, bei welcher jeweils erneut durchgezählt wird, was wirklich frei ist.
Delphi-Quellcode:
type
  TXRefInterfacedObject = Class(TObject, IInterface)
  Protected
    {IInterface}
    Function QueryInterface(Const IID: TGUID; Out Obj): HResult; StdCall;
    Function _AddRef: Integer; StdCall;
    Function _Release: Integer; StdCall;
  Protected
    FRefCount: Integer;
    Procedure DoCountingXRefs(Var Count: Integer); Virtual;
    Procedure DoFreeXRefs; Virtual;
  Public
    Procedure AfterConstruction; Override;
    Procedure BeforeDestruction; Override;
    Class Function NewInstance: TObject; Override;
    Property RefCount: Integer Read FRefCount;
  End;

Function TXRefInterfacedObject.QueryInterface(Const IID: TGUID; Out Obj): HResult;
  Begin
    If GetInterface(IID, Obj) Then Result := S_OK
    Else Result := E_NOINTERFACE;
  End;

Function TXRefInterfacedObject._AddRef: Integer;
  Begin
    Result := InterlockedIncrement(FRefCount);
  End;

Function TXRefInterfacedObject._Release: Integer;
  Var XRefs: Integer;

  Begin
    XRefs := 0;
    DoCountingXRefs(XRefs);
    Result := InterlockedDecrement(FRefCount);
    If Result = XRefs Then Destroy;
  End;

Procedure TXRefInterfacedObject.DoCountingXRefs(Var Count: Integer);
  Begin
    // do nothing
  End;

Procedure TXRefInterfacedObject.DoFreeXRefs;
  Begin
    // do nothing
  End;

Procedure TXRefInterfacedObject.AfterConstruction;
  Begin
    // release the constructor's implicit refcount
    InterlockedDecrement(FRefCount);
  End;

Procedure TXRefInterfacedObject.BeforeDestruction;
  Var XRefs: Integer;

  Begin
    XRefs := 0;
    DoCountingXRefs(XRefs);
    If FRefCount <> XRefs Then Error(reInvalidPtr);
    DoFreeXRefs;
    If FRefCount <> 0 Then Error(reInvalidPtr);
  End;

Class Function TXRefInterfacedObject.NewInstance: TObject;
  Begin
    Result := Inherited NewInstance;
    TInterfacedObject(Result).FRefCount := 1;
  End;
  • Vorteil der letzeren Variante wäre, daß man die verlinkten Interfaces befragen könnte, ob ob diese einen wirklich noch benötigen.
  • Allergings ist dieses (vorallem die Zählprozedur) nicht threadfähig, falls irgendwo anders in der Zwischenzeit deine neue Referenz erstellt oder freigegeben wird.
    Hier müßte also noch eine Absicherung rein ('ne CriticalSection z.B.), welche solange alles andere blockiert und eventuell die Threads interagieren läßt.
  • Die ersteren Beiden sind da leider nicht flexiebel (siehe Punkt 1)
    und da sie einen externen Zugriff auf die Referenzzählung erlauben, kann es sein, daß über DoFreeXRefs nicht alle der externen Referenzen freigegeben werden, wenn dieses nötig wäre.
    (hier wäre es wohl besser die Registrierungen auch nur innerhalb des Objektes zu handhaben)
  • Theoretisch wäre auch noch eine Variante mit einem LöschCallback denkbar, welchen man beim Registrieren der nichtgezählten Referenz mit angeben muß.
    (das war mir grad eben erst, beim Schreiben eingefallen ... werde hierzu demächst noch einen Beispielcode nachreichen)


So, nun seid ihr gefragt.
Wie würdet oder wie habt ihr solche Probleme gelöst,
bzw. welche Variante (kann auch eine ganz Andere sein) würdet ihr bevorzugen
und wo würdet ihr euer Pro und Contra setzen?
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
Benutzerbild von sx2008
sx2008

Registriert seit: 15. Feb 2008
Ort: Baden-Württemberg
2.332 Beiträge
 
Delphi 2007 Professional
 
#2

Re: Kreuzreferenzen - selbsthaltende Interfaces

  Alt 25. Apr 2010, 08:09
Man kann das "Sklavenobjekt" auch von TContainedObject anstatt TInterfacedObject ableiten.
Objekte dieser Klasse haben keinen eigenen Referenzzähler, sondern geben die Referenzen an ihren Master weiter.
Als Konsequenz davon muss der Master alle von TContainedObject abgeleiteten Objekte manuell mit Free freigeben wenn er selbst zerstört wird.
Das Masterobjekt und alle ContainedObjekte haben nach Aussen zwar mehrere Interfacezeiger verhalten sich aber wie ein Objekt bezügl. der Referenzzählung.
Am Besten versteht man's wenn man den Sourcecode anschaut.
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.123 Beiträge
 
Delphi 12 Athens
 
#3

Re: Kreuzreferenzen - selbsthaltende Interfaces

  Alt 25. Apr 2010, 08:51
Hey, das klingt auch nicht so schlecht.
Nur hätte ich da ein kleines "Problem", denn bei mir kann es vorkommen, daß ein Objekt als TContainedObject oder als TInterfacedObject behandelt werden mütße, jenachdem ob es einen "Master"/Parent gibt oder nicht.
Aber ich denke das läßt sich leicht lösen, indem ich einfach diese beiden Objekte kombiniere.
[edit]
Das Kombinieren der Interfaces und eine Fallunterscheidung im Create ist kein Problem,
aber wenn ich jetzt den Status wärend der Lebenszeit des Objektes ändern will/mußt, dann wird es schon schwerer. *grübel*
[/edit]

Die Variante über RegisterCallbacks scheint aber auch machbar zu sein.

Mal sehn, vermutlich erstelle ich mir erstmal diese TInterfacedObject-TContainedObject-Kombination und falls es nötig ist, integriere ich mir noch diese Callbacks.
(hab ja noch etwas Zeit ... hatte mir diese Frage schon frühzeitig gestellt, bevor ich wieder mit diesen Interfaces anfange)
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
jbg

Registriert seit: 12. Jun 2002
3.481 Beiträge
 
Delphi 10.1 Berlin Professional
 
#4

Re: Kreuzreferenzen - selbsthaltende Interfaces

  Alt 25. Apr 2010, 09:56
Zitat:
Wie würdet oder wie habt ihr solche Probleme gelöst
Ich löse das über meine Weak Interface References Unit (Delphi 2007+)
  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 15:20 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