AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Frage zu FreeAndNil

Ein Thema von DelTurbo · begonnen am 19. Feb 2010 · letzter Beitrag vom 27. Feb 2010
Antwort Antwort
Seite 3 von 3     123   
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#21

Re: Frage zu FreeAndNil

  Alt 27. Feb 2010, 15:06
Zitat:
Warum soll eine Referenz auf ein Objektfeld *in einem Destruktor* auf nil gesetzt werden?
Du beantwortest also diese Frage mit "kommt nicht vor", und das ist der Denkfehler der ganzen Diskussion.

Natürlich kann sowas vorkommen bei nicht statischen Objektvariablen. Denke dir eine hiearische Struktur aus Objekten. Jedes Objekt hat ein Feld Parent und verweist auf das übergeordnete Objekt. das Übergeordnete Objekt = parent wiederum hat mehrere Objektfelder auf andere untergeordnete Objekte wie zb. Left und Right. So sieht die Struktur eines AVL/RB Baumes aus. UU. kann also folgender Zugriff nötig werden -> Self.Parent.Left. Left vom Parent kann aber in diesem Baum durchaus nil sein. Ergo wird in diesem Design vor dem Zugriff auf .Left mit Assigned(Self.Parent.Left) abgefragt. In Parent.Free wird Parent.Left und Parent.Right mit FreeAndNil() freigegeben. Das erfolgt sequentiell im Destruktor, also

Delphi-Quellcode:
begin
  FreeAndNil(Left);
  FreeAndNil(Right);
end;
Wenn nun im Destruktor von .Right selber über Parent.Left auf Left zugegriffen werden soll dann ist dies mit Assigned(Parent.Left) abgesichert. Wird im Parent nun nicht .Left auf NIL gesetzt ist das ein Designfehler.

Zusätzlich denken wir uns den Fall das die Objekte selber mit Windows Botschaften arbeiten. Ab diesem Moment wird es ein asynchrones Design da selbst bei Destruktor eines dieser Child Objekte noch Windows Botschaften reinkommen können. Innerhalb dieser Botschaften, die nicht vorhersehbar sind aus Sicht des Entwicklers dieser Struktur, wird nun über Parent.Lef/.Right zugriffen. Natürlich wieder mit Assigned() geschützt. Wird aber die Objektreferenz auf .Left/.Right nciht sauber aufgeräumt dann ist das ein Designfehler.

Man benutzt also den Status "nil" einer Objektreferenz als zusätzlichen Status, so ähnlich wie es in Datenbank den Wert NULL eines Feldes geben kann. Um darauf sauber reagieren zu können muß man FreeAndNil() und Assigned() benutzen, auch auf Grund des oben genannten "Kaskaden" Effektes.

Fazit: es ist eher umgekehrt. Wird nicht FreeAndNil() benutzt um diese Referenzen freizugeben dann ist das ein Designfehler. Besonders im Destruktor des Parents ist das dann wichtig.

Gruß Hagen

PS: übrigens behaupte ich das du keine andere konzeptionelle Lösung für obiges Problem finden wirst. Es existiert also kein anderes Design das das so kann und ergo ist obiges Szenario in keinem Fall ein Designfehler.

PPS: ich stelle also die Gegenthese auf: FreeAndNil() im Destruktor nicht zu benutzen kann uU. ein Designfehler sein, aber es in jedem Fall zu benutzen ist es mit Sicherheit nicht.
  Mit Zitat antworten Zitat
Hawkeye219

Registriert seit: 18. Feb 2006
Ort: Stolberg
2.227 Beiträge
 
Delphi 2010 Professional
 
#22

Re: Frage zu FreeAndNil

  Alt 27. Feb 2010, 17:28
Hallo Hagen,

bei dem von dir beschriebenen Szenario stört mich, dass die Kind-Objekte in ihrem Destruktor(!) auf eventuell bereits zerstörte Geschwister-Objekte zugreifen. Dies ließe sich meiner Meinung nach verhindern, wenn das Eltern-Objekt die Freigabe über einen Aufruf "Child.Destroying" ankündigen würde. In dieser Methode könnten die Kind-Objekte auf die noch existierenden Geschwister zugreifen. Erst wenn alle Kind-Objekte auf diese Weise informiert wurden, darf das Eltern-Objekt sie freigeben. Ein "FreeAndNil" wäre dann nicht notwendig, da nach dem Aufruf der Destroying-Methode kein Zugriff der Kind-Objekte auf benachbarte Objekte mehr erfolgen darf.

Übersehe ich vielleicht etwas?

Gruß Hawkeye
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#23

Re: Frage zu FreeAndNil

  Alt 27. Feb 2010, 23:56
Ja.

Pragmatisch betrachtet kann es immer vorkommen das die Childrens über deren Parent auf die anderen Childrens zugreifen wollen, zu jedem Zeitpunkt. Das in meinem Beispiel die Objektfelder .Left und .Right auch nil sein können ist ein gezieltes Designkriterium, da sowas in einem Baum eben der Fall ist. Ergo: benutzt man den Status "nil" auch als Information.
Tödlich wird es in diesem Beispiel gerade dann wenn diese Objektreferenzen bei Freigabe der Objekte nicht sauber und sicher freigegeben werden. Das geht nur mit FreeAndNil() oder vergleichbaren Verfahren.

Die Freigabe eines Childs in solchen Bäumen ist zu jedem Zeitpunkt möglich, wenn man Informationen in den Baum hinzufügt, wegnimmt, umsortiert, merged, komprimiert oder eben den Baum komplett freigibt. Der Zustand NIL ist also ein Normalfall in diesem Beispiel.

Da es um jeden beliebigen Zeitpunkt geht ist eine Notification wie in deinem Vorschlag eben keine Lösung, diese Notification bezieht sich nur auf den Fall der Freigabe des Baumes und nicht prinzipiell zu jedem Lebenszyklus oder Operation des Baumes. Zudem würde die Struktur dieses Baumes damit verletzt. Denn nun müssten ja alle Childrens eines Baumes sich merken das die anderen Childrens zerstört wurden oder das sich der Baum in der Zerstörung befindet. Das ist wesentlich fehleranfällirer am Ende, als ein einfacher FreeAndNil() Aufruf und dem gezielten Ausnutzen das eben diese Objektreferenzen im Parent auch nil sein könnten.

Der schlimmste Designfehler meiner Meinung nach ist es ein sich gestelltes Ziel nicht mit dem geringst möglichen Aufwand sauber lösen zu wollen, sondern der Versuch durch Verkompilizierung es nur schön zu machen. Wenn das Mittel "nil setzen einer Objektrefernz" vorhanden ist und wenn es das Mittel mit dem geringsten Aufwand ist dann sollte man es nutzen.

Zitat:
bei dem von dir beschriebenen Szenario stört mich, dass die Kind-Objekte in ihrem Destruktor(!) auf eventuell bereits zerstörte Geschwister-Objekte zugreifen. Dies ließe sich meiner Meinung nach verhindern, wenn das Eltern-Objekt die Freigabe über einen Aufruf "Child.Destroying" ankündigen würde.
Naja oder noch einfacher, im Destruktor der Childs wird einfach nicht mehr auf die Objektstruktur des Baumes zugegriffen

Allerdings führte ich in meiner obigen Argumentation mit Bedacht auch die Möglichkeit der Windows Botschaftsbearbeitung, als asynchrones und nicht vorhersagbares Element, mit auf. Denn exakt solche Gegebenheiten existieren in der Praxis und dann sollte der Programmierer auch dies berücksichtigen. Der Programmier kann in diesem Moment eben nicht planmäßig davon ausgehen das selbst im Destruktor des Parents, durch untergeordnete Childs und deren Windows Botschaften, solche Zugriffe nicht stattfinden werden.

Notifications helfen da wenig und verkomplizieren dann nur alles. Zudem die Notificaions selber wiederum anfällig wären, denn auch diese sind auf gültige Objektrefernezen angewiesen. Und eine gültige Objektreferenz bezeichnet auch den Fall = nil.

In Bezug auf solche Bäume kommt es auch häufig vor das man Objektrefernezen tauschen muß, zb. bei der Umsortierung. Auch dort sollte man die gleiche Technik wie in FreeAndNil() verwenden um es sauber zu machen. Defakto wird man es auch so machen und damit ist FreeAndNil() nur ein Spezialfall unter vielen anderen.

Anders ausgedrückt: bei meinem Beispiel ist das FreeAndNil() im Destruktor nur eine kritische Operation unter anderen. Sie unterscheidet sich in keinster Weise von zb. dem Einfügen oder Entfernen neuer Objekte in den Baum. Ergo, mein Beispiel zeigt das die Beschränkung der Diskussion auf den Destruktor und FreeAndNil() im Vergleich zu einfachem .Free, eine sinnfreie Argumention sein wird um Designfehler zu bewerten. Im Gegenteil, mein Beipiel zeigt ein mögliches Design auf in dem es ein Designfehler wäre eben nicht FreeAndNil() zu benutzen. Selbst Notifications wären ungeeignet und eine zu überkompensierte Lösung des Problemes die die Fehleranfälligkeit noch inkrementieren würde, eben ebenfalls ein Designfehler.

Nun, wenn ich dieses Gegenbeispiel anführen kann, dann ist es fakt das die eigentliche Diskussion sinnfrei wird, da sich eben keine Generalisierung davon ableiten lässt, es existieren ja die Bäume zb. Und was mich eben stört ist die Aussage: FreeAndNil() nicht im Destruktor zu verwenden da man so eventuelle Designfehler vertuschen würde. Das abgleitete Indiz das nach Änderung vom .Free auf FreeAndNil() nun keine fehler mehr auftreten würden halte ich für unwahrscheinlich. Im Allgemeinen kann es sich ja nur auf illegale Speichzugriffe auf nicht mehr existente Speicherobjekte beziehen. Wurde die Referenz mit FreeAndNil() bereinigt dann treten diese AVs weiterhin auf, aber dieses Mal eben beim Zugriff auf Addresse 0x0000??. Der Designfehler wäre also nicht vertuscht nur eventuell dessen Wahrscheinlichkeit der Entdeckung reduziert. Und ob es überhaupt ein Designfehler ist sei dahin gestellt, vielleicht fehlte ja nur eine Assigned() Anfrage in anderen Programmteilen die mit FreeAndNil() das Problem sauber gelösst hätte.

Und wie ichs schon sagte: das Problem sollte beseitigt werden und nicht nur die Symptome. Wenn also ein Programmierer mit FreeAndnil() das Symptom beseitigt dann ist das eher ein Indiz für Schlampigkeit in der Arbeitsweise statt ein Hinweis auf einen Designfehler. Die gemachte Ableitung der Eingangsdiskussion ist also falsch.

Gruß Hagen
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 3 von 3     123   


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:56 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