Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Pointer existiert, das Objekt dahinter nicht. Wie zuverlässig prüfen? (https://www.delphipraxis.net/199591-pointer-existiert-das-objekt-dahinter-nicht-wie-zuverlaessig-pruefen.html)

uups 4. Feb 2019 15:42

Delphi-Version: 10.2 Tokyo

Pointer existiert, das Objekt dahinter nicht. Wie zuverlässig prüfen?
 
Hallo!

Sagen wir, es gibt ein Objekt und ein Pointer, das auf dieses Objekt zeigt. Irgendwann wird das Object freigegeben, Pointer zeigt aber immer noch darauf. Die Prüfung mit Assigned(Pointer) ist erfolgreich und erst beim Versuch, auf das Object dahinter zuzugreifen kommt die AV.

Gibt es eine zuverlässige Möglichkeit zu prüfen, ob ein Objekt hinter einem "gültigen" Pointer noch existiert?

Neutral General 4. Feb 2019 16:02

AW: Pointer existiert, das Objekt dahinter nicht. Wie zuverlässig prüfen?
 
Nein gibts nicht*

*) Vielleicht mit großem Aufwand und viel Gematsche (ich glaube wir hatten so nen Thread irgendwann schon mal) aber es lohnt sich nicht.

dummzeuch 4. Feb 2019 16:07

AW: Pointer existiert, das Objekt dahinter nicht. Wie zuverlässig prüfen?
 
Zitat:

Zitat von uups (Beitrag 1424672)
Sagen wir, es gibt ein Objekt und ein Pointer, das auf dieses Objekt zeigt. Irgendwann wird das Object freigegeben, Pointer zeigt aber immer noch darauf. Die Prüfung mit Assigned(Pointer) ist erfolgreich und erst beim Versuch, auf das Object dahinter zuzugreifen kommt die AV.

Gibt es eine zuverlässige Möglichkeit zu prüfen, ob ein Objekt hinter einem "gültigen" Pointer noch existiert?

Kurze Antwort: Nein.

Es gibt mehrere Ansätze, von denen aber keiner zuverlässig ist, denn der freigegebene Speicher kann ja inzwischen anderweitig verwendet worden sein:

1. Man könnte überprüfen, ob ClassName immernoch den erwarteten Wert liefert. Problem: Solange der Speicher nicht neu verwendet wurde, funktioniert das auch nach der Freigabe. Wurde er neu verwendet, kann das eine andere Intanz derselben Klasse sein, so dass man auch hier nicht zuverlässig herausfindet, ob es noch dasselbe Objekt ist. Und dann kann der Speicher so verwendet worden sein, dass der Aufruf von ClassName zu unvorhersehbaren Ergebnissen führt. Am häufigsten eine Access Violation, aber es sind auch andere Fehler möglich. Z.B. könnte eine völlig andere Methode aufgerufen werden, die das Bankkonto des Anwenders leerräumt, Pornos an seine Frau schickt und das BKA ruft. ;-)

2. Man könnte Felder mit bekanntem Inhalt überprüfen. Ob das funktioniert ist natürlich abhängig von der Klasse. Z.B. könnte die Länge eines dynamischen Arrays, das im Constructor gesetzt wird, überprüfen, oder ein Feld mit einem Interface oder einem String, welches im Constructor gesetzt wird. Wenn das Feld nicht den erwarteten Wert hat, ist das Objekt nicht gültig. Problem: Es könnte ein anderes Objekt dort liegen, dessen Felder zufällig ebenfalls gültige Werte haben. Auch der Zugriff auf diese Felder könnte zu Nebenwirkungen führen, z.B. AVs.

3. In Abwandlung von 2. könnte man ein Feld vorsehen und mit klassenspezifschem Inhalt + einen Instanzspezifischen Inhalt (z.B. einer laufenden Nummer) füllen. So kann man theoretisch prüfen, ob das Objekt die richtige Klasse hat und ob es noch das alte ist (Wobei letzteres voraussetzt, dass man weiss, welchen Inhalt die Instanz haben soll, woher weiss man das? Z.B. indem man neben dem Pointer immer auch die Laufende Nummer des Objekts in einem Record speichert.). Auch hier kann es zu Fehlern beim Zugriff kommen.

4. Man könnte die Verwaltungsstrukturen des Memory-Mangers durchgehen und nachssehen, ob der Pointer noch auf belegten Speicher zeigt. Hilft nur bedingt, denn der Speicher könnte ja schon wieder anderweitig verwendet worden sein.

5. Man könnte die Speicherverwaltung für seine Objekte selbst übernehmen und dafür sorgen, das Speicher niemals wieder verwendet wird. Der Destructor füllt dann den Speicher des Objekts mit z.B. 0en, so dass man tatsächlich feststellen kann, ob ein Objekt noch gültig ist. Das dürfte sogar funktionieren, aber verschwendet eine Menge Speicher.

Und all das berücksichtig noch nicht einmal Multithreading.

Oder kurz: Zuverlässig geht das nicht.

MichaelT 4. Feb 2019 16:43

AW: Pointer existiert, das Objekt dahinter nicht. Wie zuverlässig prüfen?
 
Nein.

Du musst selbst dafür sorgen, dass die Referenz auf NIL (Not in List) gesetzt wird oder gültig bleibt.

Alles andere führt zu einem Verwaltungsaufwand der noch dazu dein Klassendesign beeinträchtigt von der Programmarchitektur ganz zu schweigen. Transparent geht es nicht. Am Ende läuft jede Strategie drauf hinaus indirekte Referenzen zu halten, die Klasse am Ende ihre Objekte selbst verwaltet und den Overhead muss man aufwiegen mit der Vereinfachung anderorts bspw. beim Iterieren über Container. Proxyobjekte oder Nullobjekte usw...

Du kannst bspw. das Auftreten solcher Situation zu vermeiden mit TObjectList arbeiten. In Delphi gilt an sich das Vermeiden dieser Abfrage als geboten.



Zitat:

Zitat von uups (Beitrag 1424672)
Hallo!

Sagen wir, es gibt ein Objekt und ein Pointer, das auf dieses Objekt zeigt. Irgendwann wird das Object freigegeben, Pointer zeigt aber immer noch darauf. Die Prüfung mit Assigned(Pointer) ist erfolgreich und erst beim Versuch, auf das Object dahinter zuzugreifen kommt die AV.

Gibt es eine zuverlässige Möglichkeit zu prüfen, ob ein Objekt hinter einem "gültigen" Pointer noch existiert?


hoika 4. Feb 2019 16:48

AW: Pointer existiert, das Objekt dahinter nicht. Wie zuverlässig prüfen?
 
Hallo,
gibt es, aber nicht für lau und nur, wenn sich der gesamte beteiligte Code dran hält.

Die Objekte mit den Pointer müssen sich beim eigentlichen Objekt registrieren,
das eigentliche informiert dann, wenn es freigegeben wird.
Dann müssen die Pointer-Objekte ihren Pointer auf nil setzen.

Uwe Raabe 4. Feb 2019 21:00

AW: Pointer existiert, das Objekt dahinter nicht. Wie zuverlässig prüfen?
 
So wie TComponent das macht: System.Classes.TComponent.FreeNotification

Funktioniert halt nur mit TComponent, aber nicht mit beliebigen TObject-Abkömmlingen.

jaenicke 5. Feb 2019 06:42

AW: Pointer existiert, das Objekt dahinter nicht. Wie zuverlässig prüfen?
 
Wenn man so etwas braucht, sollte man mit Interfaces arbeiten.

Man kann auch schauen, ob man statt den Originalpointer weiterzugeben nur Kopien weiterreichen kann. Das geht nicht immer, wäre aber eine gute Lösung. So wird es ja z.B. mit TPicture schon gemacht. Da wird eine Zuweisung zu einem Assign umgebogen.

freimatz 5. Feb 2019 07:57

AW: Pointer existiert, das Objekt dahinter nicht. Wie zuverlässig prüfen?
 
Zitat:

Zitat von jaenicke (Beitrag 1424693)
Wenn man so etwas braucht, sollte man mit Interfaces arbeiten.

Tja, hilft auch nicht immer. Gerade size ich an so einem Fall. Habe ein interface das ein anderes inplementiert. Hole mit von dem Objekt das andere interface, und plötzlich ist das Objekt weg. :cry:

Uwe Raabe 5. Feb 2019 08:26

AW: Pointer existiert, das Objekt dahinter nicht. Wie zuverlässig prüfen?
 
Zitat:

Zitat von freimatz (Beitrag 1424698)
Habe ein interface das ein anderes inplementiert. Hole mit von dem Objekt das andere interface, und plötzlich ist das Objekt weg. :cry:

Dann machst du vielleicht was falsch?

dummzeuch 5. Feb 2019 08:32

AW: Pointer existiert, das Objekt dahinter nicht. Wie zuverlässig prüfen?
 
Zitat:

Zitat von freimatz (Beitrag 1424698)
Zitat:

Zitat von jaenicke (Beitrag 1424693)
Wenn man so etwas braucht, sollte man mit Interfaces arbeiten.

Tja, hilft auch nicht immer. Gerade size ich an so einem Fall. Habe ein interface das ein anderes inplementiert. Hole mit von dem Objekt das andere interface, und plötzlich ist das Objekt weg. :cry:

Lass mich raten: Du mischst Objekte und Interfaces? Also etwa so:

Delphi-Quellcode:
obj := TMyObject.Create;
Int1 := obj as IInterface1;
// oops, ich brauche ein anderes
Int1 := nil; // weg mit Int1, das brauche ich nicht mehr (*1)
Int2 := obj as IInterface2; // -> boom
*1 hier wird dein Objekt freigegeben, da es keine Interface-Referenz mehr darauf gibt.

Sowas zu mischen ist fast immer eine schlechte Idee.

Hol Dir doch von dem "falschen" Interface einfach das benoetigte.

Delphi-Quellcode:
Int2 := Int1 as Interface2;
Int1 := nil;

freimatz 5. Feb 2019 08:33

AW: Pointer existiert, das Objekt dahinter nicht. Wie zuverlässig prüfen?
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1424701)
Zitat:

Zitat von freimatz (Beitrag 1424698)
Habe ein interface das ein anderes inplementiert. Hole mit von dem Objekt das andere interface, und plötzlich ist das Objekt weg. :cry:

Dann machst du vielleicht was falsch?

Klar. An Code von anderen Leuten rum machen :wink:
Ich wollte damit nur sagen dass interfaces auch kein Allheilmittel sind. Aber ich verwende die auch gerne weil man normalerweise sich dann keine Gedanken mehr machen muss und wenn da ein Pointer noch eine Referenz drauf hält das Objekt gar nicht freigegeben wird.

jaenicke 5. Feb 2019 08:35

AW: Pointer existiert, das Objekt dahinter nicht. Wie zuverlässig prüfen?
 
Zitat:

Zitat von freimatz (Beitrag 1424698)
Tja, hilft auch nicht immer. Gerade size ich an so einem Fall. Habe ein interface das ein anderes inplementiert. Hole mit von dem Objekt das andere interface, und plötzlich ist das Objekt weg. :cry:

Das hört sich so an als ob da Objektreferenzen mit Interfacereferenzen gemischt werden. Der einfachste Fall:
Delphi-Quellcode:
procedure Example(const AData: IBlub);
begin...end;

Example(TMySuperObject.Create);
In diesem Fall geht die Referenzzählung schief, da durch das const die Referenzzählung deaktiviert wird. Nach der Erstellung des Objekts wäre es aber gut, wenn der Referenzzähler einmal erhöht würde. Gibt man nämlich innerhalb der aufgerufenen Methode (Example) das Objekt weiter oder speichert die Referenz irgendwo, ist das Objekt dahinter ggf. plötzlich weg, denn der Zähler ist dann ggf. 0.

Insofern gibt es schon Konstellationen, in denen Fehler in der Referenzzählung, die man beim Programmieren macht, nicht so einfach auffallen. Wenn man nicht schon einmal über den genannten Fall gestolpert ist, wird das vermutlich auch nicht so schnell auffallen beim Überlesen des Quelltextes.

Zitat:

Zitat von freimatz (Beitrag 1424704)
Klar. An Code von anderen Leuten rum machen :wink:
Ich wollte damit nur sagen dass interfaces auch kein Allheilmittel sind.

Es ist wie mit reinen Objekten... Solange der Quelltext sauber geschrieben ist gibt es auch kaum Probleme. ;-)

hanvas 5. Feb 2019 08:39

AW: Pointer existiert, das Objekt dahinter nicht. Wie zuverlässig prüfen?
 
Zitat:

Zitat von dummzeuch (Beitrag 1424676)

Es gibt mehrere Ansätze, von denen aber keiner zuverlässig ist, denn der freigegebene Speicher kann ja inzwischen anderweitig verwendet worden sein:

Dort wo es notwendig ist, und das ist es selten, kann man einen Ansatz verwenden der Deine Methoden kombiniert und mit sehr hoher Wahrscheinlichkeit auch funktioniert wenn Speicherbereiche nicht überschrieben bzw. überschrieben wurde.

Ich leite von einer (Ur) Klasse ab, bei der jedes Objekt eine eindeutige Identität in einem Feld und in einem anderen Feld den Zustand speichert.

Der Constructor sorgt dafür das Identität und Zustand korrekt gesetzt werden, der Destrutor überschreibt den Zustand. Da der Offset der beiden Felder im Speicher bekannt ist, kann ich auch bei freigegebenen Objekten bei denen der Speicher nocht nicht überschrieben wurde prüfen ob diese noch existieren, und bei freigegebenen überschriebenen Objekten hingegen ist sehr unwahrscheinlich das beide Felder zufällig so überschrieben werden das diese Sinn ergeben.

cu Ha Joe

freimatz 5. Feb 2019 12:13

AW: Pointer existiert, das Objekt dahinter nicht. Wie zuverlässig prüfen?
 
Geht nur bei eignen Klassen, aber nicht z.B. bei der VCL. Aber immerhin.

Erwähnen möchte ich noch, dass madexcept hat die Möglichkeit hat, Speicherzugriffe abzusichern. Damit kann man u.U. Probleme in dem Bereich finden. Allerdings ist das extrem langsam und verbraucht viel Speicher.

jus 6. Feb 2019 21:51

AW: Pointer existiert, das Objekt dahinter nicht. Wie zuverlässig prüfen?
 
Zitat:

Zitat von jaenicke (Beitrag 1424693)
Wenn man so etwas braucht, sollte man mit Interfaces arbeiten.

Man kann auch schauen, ob man statt den Originalpointer weiterzugeben nur Kopien weiterreichen kann. Das geht nicht immer, wäre aber eine gute Lösung. So wird es ja z.B. mit TPicture schon gemacht. Da wird eine Zuweisung zu einem Assign umgebogen.

Hallo,

ich muß zugeben, dass ich eigentlich noch ein Anfänger in Interfaces bin. Mich würde der Ansatz mit Interfaces sehr interessieren, wie man dann testen kann, ob das Objekt schon freigegeben wurde? Sprich, macht man dann einfach zu einem Objekt noch einen Wrapper Interface und testet man dann mit "if assigned(Interfacevariable) then", ob das Objekt bereits freigegeben wurde? :?: Könnte wer da ein kurzes Beispiel dazu posten? :-)

lg,
jus

Schokohase 6. Feb 2019 22:13

AW: Pointer existiert, das Objekt dahinter nicht. Wie zuverlässig prüfen?
 
Mit Interfaces brauchst du nicht mehr testen, denn die Instanz wird erst freigegeben, wenn du die Instanz vergessen hast.
Delphi-Quellcode:
var
  foo, foo1, foo2: IFoo;
begin
  foo := TFoo.Create;
  foo1 := foo;
  foo2 := foo;
  foo := nil;
  foo1 := nil;
  foo2 := nil; // Hier erfolgt jetzt automatisch die Freigabe der Instanz
end;
Und wenn man nicht mehr weiß wonach man sucht, wie will man dann feststellen ob es noch existiert ...

hoika 6. Feb 2019 23:07

AW: Pointer existiert, das Objekt dahinter nicht. Wie zuverlässig prüfen?
 
Hallo,
Einspruch

Zitat:

foo2 := nil; // Hier erfolgt jetzt automatisch die Freigabe der Instanz
Das glaube ich nicht (weiß es aber auch nicht).

Die Freigabe erfolgt doch erst, wenn die Interface-Variable ihren Scope verläßt?
In diesem Fall hinter dem abschließenden end?

Oder wird das durch das nil-Setzen vorgezogen?

Uwe Raabe 6. Feb 2019 23:13

AW: Pointer existiert, das Objekt dahinter nicht. Wie zuverlässig prüfen?
 
Zitat:

Zitat von hoika (Beitrag 1424914)
Die Freigabe erfolgt doch erst, wenn die Interface-Variable ihren Scope verläßt?
In diesem Fall hinter dem abschließenden end?

Eigentlich erzeugt der Compiler unsichtbaren Code der bei Verlassen des Scopes alle Interface-Variablen auf nil setzt. Man kann die Variable aber schon vorher auf nil setzen. Ob damit tatsächlich die Instanz freigegeben wird hängt davon ab ob noch andere Referenzen darauf existieren.

Übrigens muss es nicht mal nil sein. Auch das Zuweisen einer anderen Interface-Instanz an diese Variable würde den Referenzzähler der vorigen Instanz herunterzählen, so wie es den der neuen hochzählt.

Aviator 6. Feb 2019 23:14

AW: Pointer existiert, das Objekt dahinter nicht. Wie zuverlässig prüfen?
 
Zitat:

Zitat von hoika (Beitrag 1424914)
Oder wird das durch das nil-Setzen vorgezogen?

Ja wird es. Da der Referenzzähler an dieser Stelle um 1 dekrementiert wird und dadurch der Referenzzähler den Wert 0 erreicht. Ist der Zähler 0, wird das Object freigegeben. Unabhängig, ob man noch im aktuellen Scope der Function/Procedure ist oder nicht.

Mach dir doch schnell mal ein kleines Konsolenprojekt mit einer Klasse die ein Interface implementiert und den Destructor überschreibt. Dort einen Breakpoint rein und schauen, wann der Debugger dort anhält. Somit kannst du das ganz leicht testen.

jus 6. Feb 2019 23:27

AW: Pointer existiert, das Objekt dahinter nicht. Wie zuverlässig prüfen?
 
Zitat:

Zitat von Schokohase (Beitrag 1424907)
Mit Interfaces brauchst du nicht mehr testen, denn die Instanz wird erst freigegeben, wenn du die Instanz vergessen hast.
Delphi-Quellcode:
var
  foo, foo1, foo2: IFoo;
begin
  foo := TFoo.Create;
  foo1 := foo;
  foo2 := foo;
  foo := nil;
  foo1 := nil;
  foo2 := nil; // Hier erfolgt jetzt automatisch die Freigabe der Instanz
end;
Und wenn man nicht mehr weiß wonach man sucht, wie will man dann feststellen ob es noch existiert ...

ehm... mir es ist eigentlich klar, dass man bei com Interfaces der Compiler da automatisch freigibt, wenn es nicht mehr im Scope ist. Nutze ich ehrlich gesagt sehr gerne. :-D Doch die eigentliche Frage ist ja, wie man mit Hilfe von Interfaces mitten im Programm überprüfen kann, ob das Objekt noch da ist und ohne dass man das mit dem Marker auf NIL setzt. :gruebel: Mit dem NIL-Marker setzen hätte man ja kein Interface benötigt.

hoika 7. Feb 2019 00:22

AW: Pointer existiert, das Objekt dahinter nicht. Wie zuverlässig prüfen?
 
Hallo,
ich denke, die Prüfung geht auch mit Interfaces nicht,
ohne selbst eine Art Notification-Sache zu implementieren.

PS:
Im Moment erschließt sich mir die Notwendigkeit der ganzen Sache auch nicht
-> es sei denn, der Entwickler will irgendwas "faules" machen ;)

Uwe Raabe 7. Feb 2019 08:27

AW: Pointer existiert, das Objekt dahinter nicht. Wie zuverlässig prüfen?
 
Zitat:

Zitat von jus (Beitrag 1424919)
Doch die eigentliche Frage ist ja, wie man mit Hilfe von Interfaces mitten im Programm überprüfen kann, ob das Objekt noch da ist

Die Frage stellt sich bei korrekter Implementation gar nicht. Solange noch eine Interface-Variable das Interface hält ist die Instanz auch noch da. Ein Interface implementierendes Objekt wird niemals direkt freigegeben, sondern ausschließlich innerhalb eines _Release, wenn dieses den internen RefCount auf 0 setzt. Das ist der Sinn hinter den IInterface-Methoden _AddRef und _Release.

Will oder muss man diesen Weg verlassen, kommt man um irgendeinen Notification-Mechanismus nicht herum.

p80286 7. Feb 2019 10:11

AW: Pointer existiert, das Objekt dahinter nicht. Wie zuverlässig prüfen?
 
Zitat:

Zitat von hoika (Beitrag 1424920)
Hallo,
ich denke, die Prüfung geht auch mit Interfaces nicht,
ohne selbst eine Art Notification-Sache zu implementieren.

PS:
Im Moment erschließt sich mir die Notwendigkeit der ganzen Sache auch nicht
-> es sei denn, der Entwickler will irgendwas "faules" machen ;)

Wieso? Ein Pointer ist eine Adresse, es ist schon von Interesse, ob sich an der Adresse das befindet, was man erwartet. Würde der TE mit "normalen" Variablen oder normierten Schnittstellen arbeiten würde sich auch die Frage nicht stellen.
Ich vermute er hat irgendein Pointer-Voodoo gelesen und versucht das an einem untauglichen Objekt umzusetzen.

Gruß
K-H


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