Delphi-PRAXiS
Seite 1 von 6  1 23     Letzte »    

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Referenzen auf ungültige Objekte (https://www.delphipraxis.net/159095-referenzen-auf-ungueltige-objekte.html)

stahli 14. Mär 2011 13:31

Referenzen auf ungültige Objekte
 
(Ich habe es schon mal angesprochen, will es aber nochmal strukturierter angehen.)

Das Problem:
- Objekt O ist instantiiert.
- Eigenschaft X.MyO zeigt auf O.
- Variable MyO zeigt ebenfalls auf O.

Wird Objekt O freigegeben (vielleicht sogar mit FreeAndNil(O)) zeigen X.MyO und MyO auf einen "ungültigen" Speicherbereich.

Eine Lösung sollen Interfaces bzw. Referenzzähler bieten. Durch diese wird O tatsächlich erst freigegeben, wenn die Referenzen von X.MyO und MyO quasi abgemeldet werden.
Wie ich das verstanden habe, funktioniert dies so mit Interfaces im Delphi sowie auch mit der GarbageCollection im .NET.
Das läuft auch unter dem Begriff "SmartPointer".

Soweit richtig?

Das ist aber noch nicht der Ansatz, den ich mir wünschen würde.
Ich hätte gern, dass X.MyO und MyO bei O.Free automatisch auf nil gesetzt werden.

Derzeit löse ich das in meinem Projekt händisch (durch die Verwaltung von eigenen Listen), da kann es jedoch passieren, dass ich einige Objekte vergesse und später nachtragen muss.

Daher zwei Fragen:

1) Könnte/Sollte Delphi (optional) nicht eine Funktion bieten, die eine solche Verwaltung übernimmt?
Man könnte Objekte regestrieren, die "überwacht" werden sollen:
Delphi-Quellcode:
RegisterValidObject(O)
Das Objekt O wird in eine Liste aufgenommen. Wird das Objekt freigegeben, wird es aus der Liste entfernt.
Dann könnte man mit
Delphi-Quellcode:
IsValidObject(X.MyO)
bzw.
Delphi-Quellcode:
IsValidObject(MyO)
prüfen, ob das Objekt noch gültig (noch in der Kontroll-Liste enthalten) ist.
Das wäre so ähnlich zu sehen, wie
Delphi-Quellcode:
Assigned(X.MyO)
, wobei nicht nur auf nil geprüft wird, sondern auf die Existenz des referenzierten Objektes.

2) Weiterhin wäre es wünschenswert, wenn die Referenzen automatisch "genilt" würden.
Dazu müsste wohl zusätzlich verwaltet werden, ob eine Variable, ein Objektfeld oder eine Objekteigenschaft das zu überwachende Objekt referenziert.
Bei der Freigabe von O müssten alle diese registrierenten Referenzen automatisch auf nil gesetzt werden.


Im Moment realisiere ich den zweiten Weg selbst durch Nutzung der neuen RTTI-Möglichkeiten.
Ich durchsuche meine Objekte nach überschreibbaren Eigenschaften, die auf Objekte zeigen, prüfe, ob das betreffende Objekt noich existiert und setze andernfalls diese Eigenschaft auf nil.
Das funktioniert, ist aber auf meine eigenen Objekte beschränkt und berücksichtigt keine Variablenreferenzen.

Eine Delphi-native Lösung hielte ich für nützlich und grundsätzlich wohl auch für realisierbar.
Wie seht Ihr das?

WM_CLOSE 14. Mär 2011 13:35

AW: Referenzen auf ungültige Objekte
 
Man könnte ein TSmartObject einführen, aber dann müssten zwangsweise alle Klassen, die den SmartPointer unterstützen von ebendieser Klasse abstammen.
Ich weiß nicht, ob man mit der RTTI so weit kommt.

r2c2 14. Mär 2011 19:05

AW: Referenzen auf ungültige Objekte
 
Zitat:

Zitat von stahli (Beitrag 1088351)
Wird Objekt O freigegeben (vielleicht sogar mit FreeAndNil(O)) zeigen X.MyO und MyO auf einen "ungültigen" Speicherbereich.

richtig.

Zitat:

Eine Lösung sollen Interfaces bzw. Referenzzähler bieten.
Ja, kann man dafür nehmen.

Zitat:

Durch diese wird O tatsächlich erst freigegeben, wenn die Referenzen von X.MyO und MyO quasi abgemeldet werden.
So in etwa. Ja.

Zitat:

Wie ich das verstanden habe, funktioniert dies so mit Interfaces im Delphi sowie auch mit der GarbageCollection im .NET.
Is AFAIK anders realisiert, aber so kannst du dirs vorstellen.

Zitat:

Das läuft auch unter dem Begriff "SmartPointer".
Den Begriff "smart pointer" kenn ich eher aus C++. Da beschreibt er ein Konzept mit ähnlichem Zweck. Interfaces würde ich aber erstmal nicht als smart pointer bezeichnen, auch wenn der Effekt ähnlich ist.

Zitat:

1) Könnte/Sollte Delphi (optional) nicht eine Funktion bieten, die eine solche Verwaltung übernimmt?
Man könnte Objekte regestrieren, die "überwacht" werden sollen:
Delphi-Quellcode:
RegisterValidObject(O)
Das Objekt O wird in eine Liste aufgenommen. Wird das Objekt freigegeben, wird es aus der Liste entfernt.
Dann könnte man mit
Delphi-Quellcode:
IsValidObject(X.MyO)
bzw.
Delphi-Quellcode:
IsValidObject(MyO)
prüfen, ob das Objekt noch gültig (noch in der Kontroll-Liste enthalten) ist.
Das wäre so ähnlich zu sehen, wie
Delphi-Quellcode:
Assigned(X.MyO)
, wobei nicht nur auf nil geprüft wird, sondern auf die Existenz des referenzierten Objektes.
Prinzipiell möglich. Das ist aber doch genau das, was du momentan tust. Oder hab ich dich da falsch verstanden?

Zitat:

2) Weiterhin wäre es wünschenswert, wenn die Referenzen automatisch "genilt" würden.
Dazu müsste wohl zusätzlich verwaltet werden, ob eine Variable, ein Objektfeld oder eine Objekteigenschaft das zu überwachende Objekt referenziert.
Bei der Freigabe von O müssten alle diese registrierenten Referenzen automatisch auf nil gesetzt werden.
Und das ist ohne erheblichen Aufwand eben nicht möglich. Können die neuen records eigentlich auch Destruktoren haben? Wenn ja, könnte man sich damit mehr oder weniger die oben erwähnten Smart Pointer nachbauen. Das wäre dann zumindest sowas Ähnliches.

Zitat:

Eine Delphi-native Lösung hielte ich für nützlich und grundsätzlich wohl auch für realisierbar.
Wie seht Ihr das?
Jetzt kommt meine eigentlich Antwort auf deine Frage: Ich halte das für in den meisten Fällen unnötig. Wenn du einen sauberen Ansatz hast, brauchst du das alles nicht. Insbesondere halte ich deine Workarounds für Frickellösungen, die mehr Probleme schaffen als lösen. Ich will dein Vorhaben damit nicht generell verteufeln. Manchmal braucht man eben - leider - Frickllösungen. Ich denke aber, du solltest dir nochmal genau überlegen, ob du nicht ein konzeptionelles Problem hast. Sag mal was zu deinen Objekten. Welche sind es, warum brauchst du die und warum kommt es zu diesen Problemen mit dem Freigeben (also nicht technisch, sondern inhaltlich gesehen)? Vielleicht gibts ja eignen besseren Weg...

mfg

Christian

Bernhard Geyer 14. Mär 2011 19:50

AW: Referenzen auf ungültige Objekte
 
Mach es doch wie auch tausendfach in der VCL gemacht wird. Wenn du eine Zeiger auf ein objekt hast trägst du dich in die Notify-Liste für die Freigabe ein. Diese wird vor der Freigabe des Objekts durchlaufen und das andere Objekt kann seinen Member nillen.

WM_CLOSE 14. Mär 2011 20:32

AW: Referenzen auf ungültige Objekte
 
man sollte auch nicht vergessen, dass die "Pointer" in -NET keine klassischen Cardinal sind, sondern Klassen, so wie fast alles in .NET.
Da lassen sich Zeiger wunderbar um einen Notifier erweitern, dass allen anderen Zeigern sagt, dass sie jetzt ungültig sind.
Aber in Delphi sind die Zeiger ja Passiv.
Man könnte über Records diese .NET-Zeiger nachbauen, aber mit der IDE kann man dann vermutlich nicht mehr rechnen:
d.h. Kein InteliSense usw. Das Prob hatten wir scon mit den neuen Generics - andauernd fehler, die gar keine waren...
Und die Destruktoren für Records muss man dem Compiler auch erst mal einhämmern.(Aber gehen tut es)

stahli 14. Mär 2011 21:07

AW: Referenzen auf ungültige Objekte
 
@r2c2:
Mein Projekt arbeitet komplett mit Objekten. Diese beinhalten die Daten und die Geschäftslogik.
Obendrauf gibt es eine GUI-Ebenene zur Darstellung und Bearbeitung der Daten.

Die Objekte halten Referenzen aufeinander (ein Verein verwaltet eine Liste von Mitgliedern, jedes Mitglied enthält eine Instanz einer Person. Jedes Spiel hat zwei Spielparteien. Jede Spielpartei hat ein oder zwei Spieler. Jeder Spieler hat eine Referenz (keine Instanz) auf eine Person. Eine Funktion ermittelt übergeordnete Komponenten - Z.B. ein Turnier, in dem ein bestimmtes Spiel enthalten ist).

Wird z.B. ein Personenobjekt gelöscht, müssen die anderen Komponenten, die eine Referenz darauf enthalten, diesen ungültigen Zeiger nilen.

Ich habe eine Lösung aus den beiden o.g. Punkten 1 + 2 realisiert. Dies funktioniert für meine Objekte schon sehr gut.

ABER ich halte z.B. auch in einem dynamischen Formular in einer Eigenschaft eine Referenz z.B. auf ein Turnierobjekt.

Wird dieses Turnierobjekt freigegeben, muss ich diese Formulareigenschaft explizit nilen - sonst geht das ggf. schief.

Wenn es dafür eine allgemeine Lösung gäbe, fände ich das hilfreich.


@Bernhard:
Das gibt es aber doch nur in Listen, meine ich!?
Oder kannst Du eine Lösung für mein o.g. Beispiel zeigen?


@WM_CLOSE:
Der Umweg über Records hört sich kompliziert an.
Ich hatte die Hoffnung, dass der Compiler (optional) einfach eine Liste der Referenzen anlegt und bei späteren Zugriffen "korrigiert".
Im Grunde mache ich das ja schon für meine Objekte, das ist aber noch nicht allgemein genug einsetzbar.

alzaimar 15. Mär 2011 03:14

AW: Referenzen auf ungültige Objekte
 
Ich kenne dein Konzept nicht, halte aber Referenzen, die -wupps- plötzlich invalid werden können, für kein gutes Design, auch wenn ich die Referenzen per Notify auf Nil setzen kann.

Deine Programmlogik kann dann an beliebigen Stellen gegen die Wand fahren. Du weisst nie, wann das der Fall sein wird, vielleicht sogar während einer elementaren Operation...

Wenn Du dir sicher bist, das dieser verteilte Zugriff
Du könntest diese Objekte auch über das Handle-Konzept realisieren:
Du instantiierst ein Objekt und bekommst von einer zentralen Vewaltung ein Handle zurück.

Du verwendest das dahinterliegende Objekt, indem Du es per Handle anforderst und automatisch gegen instantane Freigabe sperrst. Zum Ende der Verwendung gibst Du die Anforderung wieder frei.

Die Freigabe ist dann ein "gib es dann frei, wenn alle Anforderungen zurückgegeben wurden".

Du müsstest im Code nur dafür sorgen, das die Rückgabe so schnell wie möglich geschieht.

Delphi-Quellcode:
MyObject := ObjectManager.AcquireObjectByHandle(MyHandle);
If MyObjecty<>Nil Then
  Try
    DoSomethingWith(MyObject);
    DoSomethingElseWith(MyObject);
  Finally
    ObjectManager.ReleaseObjectByHandle(MyHandle);
    // MyObject ist nun nicht mehr gültig
  End
Else
  Raise EHonestlyIDontKnowHowToHandleThis.Create;

Lemmy 15. Mär 2011 06:13

AW: Referenzen auf ungültige Objekte
 
Guten Morgen,

was spräche denn dagegen hier das Observer-Pattern einzusetzen? Der Observer wird vom Subject informiert wenn dieses freigegeben wird. Damit kann der Observer entsprechend reagieren und die Referenz entfernen...

Grüße

Blup 15. Mär 2011 12:19

AW: Referenzen auf ungültige Objekte
 
Zitat:

Zitat von Bernhard Geyer (Beitrag 1088471)
Mach es doch wie auch tausendfach in der VCL gemacht wird. Wenn du eine Zeiger auf ein objekt hast trägst du dich in die Notify-Liste für die Freigabe ein. Diese wird vor der Freigabe des Objekts durchlaufen und das andere Objekt kann seinen Member nillen.

Zitat:

Zitat von Lemmy (Beitrag 1088527)
Guten Morgen,

was spräche denn dagegen hier das Observer-Pattern einzusetzen? Der Observer wird vom Subject informiert wenn dieses freigegeben wird. Damit kann der Observer entsprechend reagieren und die Referenz entfernen...

Grüße

Das ist ein klassischer Fall für das Observer-Pattern und in der VCL auch so realisiert.
In allen existierenden Objekten nach einer Referenz zu suchen, halte ich für einen Designfehler.

r2c2 15. Mär 2011 16:01

AW: Referenzen auf ungültige Objekte
 
Zitat:

Zitat von stahli (Beitrag 1088486)
Mein Projekt arbeitet komplett mit Objekten. Diese beinhalten die Daten und die Geschäftslogik.
Obendrauf gibt es eine GUI-Ebenene zur Darstellung und Bearbeitung der Daten.

Das ist soweit vollkommen normal.

Zitat:

ein Verein verwaltet eine Liste von Mitgliedern,
OK.

Zitat:

jedes Mitglied enthält eine Instanz einer Person.
Huch. Wieso das? Ich denke doch eher ein Mitglied *ist* eine Person.

Zitat:

Jedes Spiel hat zwei Spielparteien. Jede Spielpartei hat ein oder zwei Spieler.
OK.

Zitat:

Jeder Spieler hat eine Referenz (keine Instanz) auf eine Person.
Und wieder: Ich denke eher ein Spieler *ist* eine Person.

Zitat:

Eine Funktion ermittelt übergeordnete Komponenten - Z.B. ein Turnier, in dem ein bestimmtes Spiel enthalten ist).
Versteh ich nicht.

Zitat:

Wird z.B. ein Personenobjekt gelöscht, müssen die anderen Komponenten, die eine Referenz darauf enthalten, diesen ungültigen Zeiger nilen.
Hier solltest du dir genau überlegen, was die Semantik des Löschens ist. Warum wird eine Person überhaupt gelöscht? Und wer tut das? Und was genau soll da passieren? Solche Probleme lösen sich oft durch die Beantwortung der folgenden Fragen:
- Zu wem gehört das Objekt?
- Wer ist dafür zuständig es frei zu geben?
- Wann und warum wird das Objekt freigegeben?

Im Idealfall hat man die folgenden beiden Fälle:
- Ein Objekt wird nur innerhalb einer Methode benutzt ==> Dann Create und Free mit try..finally in der Methode
- Ein Objekt gehört zu einem anderen Objekt (Komposition) ==> Dann create im Konstruktor und Free im Destruktur.

Dadurch ergeben sich Baumstrukturen von "gehört-zu-Beziehungen" und man hat i.d.R. keine Probleme mit der ganzen Freigeberei. Wenn man jetzt doch mal "außer der Reihe" ein Objekt freigeben will, stellt sich wie immer die Frage wer dafür zuständig ist und ob der weiß, wo überall Referenzen rumfliegen. Wenn du n Klassendiagramm postest, könnt ich mal drübergucken. Ich vermute, dass da was mit den Zuständigkeiten nicht passt. Observerpattern udn Events wurden bereits genannt. Das sind Möglichkeiten die Kopplung zu reduzieren und so das Problem gar nicht erst entstehen zu lassen.

//Nachtrag:
@WM_CLOSE: Seit wann sind Pointer in .NET Klassen? Ich hab zwar schon lang kein .NET mehr gemacht, aber das wär mir neu.

mfg

Christian


Alle Zeitangaben in WEZ +1. Es ist jetzt 20:51 Uhr.
Seite 1 von 6  1 23     Letzte »    

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