Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Frage zu FreeAndNil (https://www.delphipraxis.net/147947-frage-zu-freeandnil.html)

DelTurbo 19. Feb 2010 14:40


Frage zu FreeAndNil
 
Hi,

ist das nicht egal ob ich object.free; mache oder FreeAndNil(object)? Die sachen liegen auf dem stack. Das letzte command bevor die sub verlassen wird, ist object.free;

Macht es sinn sachen die nicht auf dem stack liegen mit FreeAndNil freizugeben? Macht das nicht auch nur ein .free und dann object:=nil?

Danke im voraus

Neutral General 19. Feb 2010 14:45

Re: Frage zu FreeAndNil
 
Lokale Objektvariablen die am Ende der Methode/Prozedur freigegeben werden sollen brauchen kein FreeAndNil.

FreeAndNil ist eigentlich nur notwendig, wenn du einer Objektvariable im Laufe der Zeit immer neue Objekte zuweist um nachschauen zu können, ob gerade ein Objekt zugewiesen ist oder nicht.

DelTurbo 19. Feb 2010 14:49

Re: Frage zu FreeAndNil
 
Ah, danke. Wieder was gelernt :) Dann kann ich ja alles so lassen. Weil nach nil frage ich nirgends ab.

Luckie 19. Feb 2010 15:50

Re: Frage zu FreeAndNil
 
Ich habe mir aber trotzdem angewöhnt FreeAndNil zu benutzen, dann ist man immer auf der sicheren Seite.

himitsu 19. Feb 2010 15:59

Re: Frage zu FreeAndNil
 
Wenn man irgendwo prüft, ob das Objekt existiert, z.B. ala if assigned(obj) then, dann muß man die Variable nach dem Freigeben auch auf NIL setzen.

dieses gibt nur frei
Delphi-Quellcode:
obj.Free;
dieses würde zwar theoretisch auf NIL setzen, aber wenn es beim .Free zu einer Exception kommt, dann gibt es später womöglich nette Probleme
Delphi-Quellcode:
obj.Free;
obj := nil;
also macht sich Dieses dann schon besser, weil es erst auf NIL setzt und danach .Free aufruft
Delphi-Quellcode:
FreeAndNil(obj);

DelTurbo 19. Feb 2010 16:04

Re: Frage zu FreeAndNil
 
Hmmm, denn frag ich mal gezielter.

Was macht FreeAndNil? Mehr als

Delphi-Quellcode:
object.free;
object:=nil;
mach es doch nicht, oder testet der noch irgendwas? Weil wenn nicht, ist es in meinem falls wurscht, da es auf dem stack liegt und nachdem ret eh nimmer verfügbar ist.

EDIT: da war himitsu schneller :P

himitsu 19. Feb 2010 16:29

Re: Frage zu FreeAndNil
 
Zitat:

Zitat von DelTurbo
EDIT: da war himitsu schneller :P

:tongue:

Strg + Linksklick auf FreeAndNil
Delphi-Quellcode:
procedure FreeAndNil(var Obj);
var
  Temp: TObject;
begin
  Temp := TObject(Obj);
  Pointer(Obj) := nil;
  Temp.Free;
end;
was logisch gesehn in etwa Diesem entsprechen würde
Delphi-Quellcode:
procedure FreeAndNil(Obj: TObject);
begin
  try
    Obj.Free;
  finally
    Obj := nil;
  end;
end;

thkerkmann 19. Feb 2010 17:39

Re: Frage zu FreeAndNil
 
Zitat:

Zitat von himitsu
was logisch gesehn in etwa Diesem entsprechen würde
Delphi-Quellcode:
procedure FreeAndNil(Obj: TObject); <<<<<< fehlt hier ein VAR
begin
  try
    Obj.Free;
  finally
    Obj := nil;
  end;
end;

das VAR bei dieser Parameterübergabe ist aber eintscheidend. Sonst wird das if Assigned(obj) wieder nix :drunken:

mjustin 19. Feb 2010 17:59

Re: Frage zu FreeAndNil
 
Zitat:

Zitat von DelTurbo
Macht es sinn sachen die nicht auf dem stack liegen mit FreeAndNil freizugeben? Macht das nicht auch nur ein .free und dann object:=nil?

Was eh nicht mehr benutzt wird, z.B. was im Destruktor freigegeben wird, benötigt kein FreeAndNil:

Eine schöne, lange Diskussion dazu hier:

"FreeAndNil is often sign of wrong design - did anyone really read the blog?"
https://forums.embarcadero.com/threa...threadID=32623

Viele Grüße,

negaH 19. Feb 2010 18:23

Re: Frage zu FreeAndNil
 
Darüber lässt sich streiten.

Szenario:

Ein Objekt verwaltet mehrere Objektvariablen als Felder in sich selbst. Alle diese Objekte greifen über einen zb. Owner/Parent auf diese übergeordnete Objekt zu. Diese untergeordneten Objekte nutzen ihren Owner/Parent um ihrerseits auf die anderen Objekte des Parten/Owners zugreifen zu können. Soweit ein ganz legales und OOP konformes Konstrukt.

Der Parent kann nun nicht zum gleichen Zeitpuinkt seine Objektfelder freigeben, er wird es immer sequentiell ausführen müssen.
Die untergeordneten Objekte des Parents fragen ihrerseits mit Assigned(Parent.Objekt_A) bei einem Zugriff ab ob das gewünschte Objekt überhaupt erzeugt wurde.

Nun passiert es gerade bei komplexeren Klassenstrukturen wie zb. Windows Controls das ein asynchrones Messagehandling benutzt wird. Also selbst beim Freigeben eines spezifischen Objektes kann es durchaus sein das dieses über Parent.Objekt_A noch weitere Behandlungen durchführt. Würde man im Destructor des parents nun njicht mit FreeAnNil() die Objekte freigeben dann funktioniert bei der Freigabe des nachfolgenden Objektes (Objekt_B im Parent) eine Abfrage wie Assignend(Parent.Objet_A) nicht korrekt.

Man kann nun behaupten das das alles "schlechtes Design" wäre.

Ich stehe auf folgendem Standpunkt:

1.) mache gutes Design
2.) benutzte ein zusätzlich Sicherheitsnetz, das kann nie schaden und kostet nichts

Wer von sich behauptet er benötigt kein
- FreeAndNil()
- if Assigned() then
- if Objekt is Klasse then

weil sein Design immer ein gutes Design ist das dies ja nicht nötig hat, der hat noch nie im Team gearbeite, noch auf Sourcen/Code anderer Programmierer aufsetzten, weiterentwickeln müssen und ist ergo arrogant und fern der Realitäten.

Gruß Hagen

PS: oder er benutzt eine Programmiersprache bei der all diese Probleme erst garnicht auftreten können, wozu Delphi nunmal nicht gehört.

mjustin 19. Feb 2010 18:39

Re: Frage zu FreeAndNil
 
Aus: "FreeAndNil is often sign of wrong design - did anyone really read the blog?"
https://forums.embarcadero.com/threa...threadID=32623

Zitat:

Hence, Allen's assertion that, if calling FreeAndNil in a destructor
fixes your crash problems, there must be a design problem.
Frei übersetzt: "wenn man feststellt, dass man Crash-Probleme mit FreeAndNil im Destruktor in den Griff bekommt, dann ist das ein Hinweis auf ein Design-Problem".

Wenn man nun laufend solche FreeAndNil & Co. Absicherungen benötigt, weil die Entwickler sich nicht einig sind, wo das Problem - die Ursache der Crashs - liegt (oder es nicht verstehen), mag das ja gut sein um ein Softwareprodukt erst mal über Wasser zu halten. Es sollte aber dennoch versucht werden, die Ursache zu finden und durch geeignete Änderungen am Design in den Griff zu bekommen. Sonst rostet das Produkt unter dem Lack weiter vor sich hin, oder die vielen Tesafilm-Korrekturen die es zusammenhalten machen es immer schwieriger, es zu warten.

[man kann auch übermäßig defensiv programmieren und jede Zeile mit try ... except klammern (wobei im except Fall dann nichts getan wird). Das habe ich in Code schon gesehen und gefixt, der höllisch viele Exceptions erzeugte und daher entsprechend lahm lief]

Und, ja, es gibt Programmiersprachen die da robuster sind, aber manche von uns sind nun mal mit Delphi verheiratet :)

Cheers,

mkinzler 19. Feb 2010 18:53

Re: Frage zu FreeAndNil
 
http://blogs.embarcadero.com/abauer/2010/02/16/38916/

negaH 20. Feb 2010 14:34

Re: Frage zu FreeAndNil
 
So lange es in der Programmiersprache kein implizites Feature gibt das die Gültigkeit von Objektreferenzen automatisiert regelt hat FreeAndNil() eine Existenzberechtigung. Es lassen sich immer sinnvolle Anwendungsbeispiele für FreeAndNil() konstruieren wenn dies nicht so ist. Klar, man kann auch auf FreeAndNil() verzichten und baut dann eben umständlich einen aufwendigen und damit fehleranfälligen Mechanismus um auf FreeAndNil() verzichten zu können, zb. Notifications usw. Denoch bleibt am Ende die Erkenntis das FreeAndNil() für eine Sprache die keine automatische Bereinigung von Referenzen bietet eben eine Notwendigkeit ist.

Betrachtet man es so so wird auch ersichtlich wie im Grunde sinnlos diese Diskussion ist. Die saubere Lösung des Problemes ist die Änderung der Sprachfeatures, nur das halte ich für konstruktiv. Alleine schon die Konzentration, eg. Beschränkung der ganzen Diskussion auf FreeAndNil() innerhalb des Destructors, als Indiz für einen Designfehler, zeigt das diese Diskussion auf schwachen Beinen steht. Es ist unbestritten das auf Grund von Designfehlern die Problematik von FreeAndNil() im Destructor sich auftut, aber darauf ist das Problem nicht allgemein beschränkt und es berücksichtigt eben auch nicht das gerade Designfehler in der Produktion die meist gemachten Fehler mit den größten Konsequenzen sind. Dh. Designfehler wird es immer geben egal ob man FreeAndNil() und Destructoren benutzt oder eben eine Programmiersprache die auf Grund ihrer Konstruktion garnicht dieses spezielle Problem haben wird.

Ergo: ich bleibe bei meiner Aussage

1.) Designfehler vermeiden
2.) FreeAndNil() benutzen, da man Designfehler nie zu 100% ausschließen kann
3.) wenn erforderlich mit bedingten Assertions() arbeiten um die Fehlersuche zu verbessern

Diese Arbeitsweise wird uns aufgezwungen da die benutzte Programmiersprache eben Defizite hat.

Designfehler kann man allein schon auf Grund der flexiblen Zielsetzungen die in der Praxis vorkommen garnicht ausschließen. Ich möchte den Auftraggeber sehen der nach ein Jahr Entwicklung und nach geänderten Anforderungen an die Software, die eben auch grundsätzliche Änderungen im Design bedingen, nun freiwillig einsieht das oft die komplette Software von Grund auf neu entwickelt werden muß, also nochmals ein Jahr an Entwicklung nötig wird. Und mir soll keiner kommen das man dann dem Auftraggeber verklickern soll auf seine Änderungswünsche nicht eingehen zu können. Man wird eher ziemlich schnell weg vom fenster sein und keine weiteren Aufträge mehr bekommen.

Man kann also über den Idealfall diskutieren wie man möchte die Praxis kennt den Idealfall garnicht, und wir Entwickler sollten Pragmaten und keine Dogmaten sein.

Gruß Hagen

Dezipaitor 20. Feb 2010 15:02

Re: Frage zu FreeAndNil
 
FreeAndNil sollte man schon bevorzugt benutzen, jedoch sollte man es nicht alls Allheilmittel betrachten.
D.h. wenn es eine Zugriffsverletzung gibt, weil man xy.Free benutzt und nach FreeAndNil plötzlich alles wunderbar funktioniert, dann sollte man es nicht dabei belassen.

negaH 26. Feb 2010 11:21

Re: Frage zu FreeAndNil
 
Versteh ich nicht, bzw. das ist unlogisch.

zwei Szenarien:

1.) Objekt.Free lösst Exception aus:

Wird stattdessen mit FreeAndNil(Objekt) gearbeitet wird es denoch diese Exception geben. Ich wüsste nicht das FreeAndNil() intern Exceptions abfängt. So oder so wird man also eine Exception bekommen, wenn sie in .Free auftritt dann auch in FreeAndNil(). Der Fall das Objekt = nil ist mal aussen vorgenommen da FreeAndNil() identisch zu if Objekt <> nil then Objekt.Free ist.

2.) nach Objekt.Free wird Exception ausgelösst aber bei FreeAndNil() nicht mehr.

Sicheres Indiz dafür das das Program im späteren Ablauf nochmals auf Objekt zugreift obwohl es zerstört wurde. Der Programmierer hat aber schon Vorsorge beim Zugriff auf Objekt mit Assigned(Objekt) getroffen.

Damit lösst FreeAndNil() definitiv und sauber das entstandene Problem. Mehr noch, da FreeAndNil() die Objektreferenz vor dem .Free Aufruf erstmal in eine temp. Variable kopiert und sofort die Referenz auf nil setzt, vermeidet FreeAndNil() eine Kaskade von Fehlern beim Aufruf von .Free. Es ist also sicherer als das Equivalent von

Delphi-Quellcode:
if Objekt <> nil then
begin
  Objekt.Free;
  Objekt := nil;
end;
Wenn nämlich Objekt.Free eine Exception auslösst dann wird die Variable nicht mehr auf nil gesetzt. Das macht FreeAndNil() anders. Eigentlich müsste die Funktion NilAndFree() heissen.

Welche anderen Fehlerquellen für Objekt.Free im Unterschied zu FreeAndNil() sollte es noch geben ?

Alle anderen Exceptions können wir ausklammern und könnten Designfehler sein, richtig, aber sie haben nichts mehr mit Objekt.Free und FreeAndNil() im Zusammenhang gesehen zu tuen.

Ergo: dämliche Diskussion da es keinen Bezug von FreeAndNil() im Unterschied zu Objekt.Free zu einem Designfehler gibt. Denn wenn Designfehler die Ursache des Problems sein sollten dann werden sie mit FreeAndNil() genauso wenig gelösst wie mit einfachem Objekt.Free. Auch wenn dann bei FreeAndNil() erstmal scheinbar eine spezifische Exception nicht mehr auftritt so ist denoch das Program garnicht darauf vorbereitet mit Objektrefernezen die nil sind umzugehen und wird später eben dort Exceptions auslösen, eben Designfehler. Es ist also irrelevant ob man Objekt.Free im Vergleich zu FreeAndNil() betrachtet, der Designfehler wäre die Ursache und daran muß man arbeiten.

Nun, wenn das die Kernaussage der ganzen Diskussion sein sollte dann sage ich nur -> trivial, das sollte jeder halbwegs logisch denkende Mensch sofort begriffen haben. Beseitige die Ursache des Problems statt an den Symptomen rumzudoktern (naja, unsere Prolitiker machen das ja nur so).

Gruß Hagen

himitsu 26. Feb 2010 11:32

Re: Frage zu FreeAndNil
 
Zitat:

Delphi-Quellcode:
if Objekt <> nil then
begin
  Objekt.Free;
  Objekt := nil;
end;

Eigentlich reicht ja
Delphi-Quellcode:
Objekt.Free;
Objekt := nil;
Dieses ( if Assigned(Objekt) then )
Delphi-Quellcode:
if Objekt <> nil then
wird ja in .Free geprüft.

FreeAndNil prüft dieses auch nicht mehr, da es inter wiederum .Free aufruft, welches das ja prüft.
(aber im Prinzip isses ja egal)



Aber das mit der "Kaskade" stimmt schon, da durch FreeAndNil einfach sichergestellt ist, daß die Objekt-Variable auf nil gesetzt wird.
Delphi-Quellcode:
// Objekt := nil; >> wird sicher auf nil gesetzt
// Objekt.Free;   >> exception

Temp := Objekt;
Objekt := nil; >> wird sicher auf nil gesetzt
Temp.Free;     >> exception
Hier ist dieses eben nicht sichergestellt, wie negaH es eben so schön erklärte
Delphi-Quellcode:
Objekt.Free;   >> exception
Objekt := nil; >> wird NICHT mehr auf nil gesetzt,
                >> da der Programmablauf vorher abbricht

s.h.a.r.k 26. Feb 2010 12:21

Re: Frage zu FreeAndNil
 
Zitat:

Zitat von Luckie
Ich habe mir aber trotzdem angewöhnt FreeAndNil zu benutzen, dann ist man immer auf der sicheren Seite.

Luckie hat da schon recht. Egal wo, ich verwende auch FreeAndNil, obwohl man die SysUtils-Unit dann einbinden muss. Ich weiß, dass die Referenz auf das Objekt danach auf jeden Fall nil ist und zudem sollte das Objekt im Normalfall auch freigegeben sein.

mjustin 26. Feb 2010 13:21

Re: Frage zu FreeAndNil
 
Zitat:

Zitat von negaH
Auch wenn dann bei FreeAndNil() erstmal scheinbar eine spezifische Exception nicht mehr auftritt so ist denoch das Program garnicht darauf vorbereitet mit Objektrefernezen die nil sind umzugehen und wird später eben dort Exceptions auslösen, eben Designfehler.

Soweit ich es verstanden habe, geht es nicht um spezifische, sondern um 'unspezifische', d.h. irreguläre Exceptions, von denen man nicht auf die eigentliche Fehlerursache schliessen kann, seltsame Crash-Meldungen also. Es geht in dem erwänten Newsgroup-Thread nur um Crashs, die durch Verwendung von FreeAndNil im Destruktor 'unterdrückt' werden.

negaH 27. Feb 2010 10:03

Re: Frage zu FreeAndNil
 
@himitsu:

Ist mir bewusst das .Free intern Self auf nil prüft, das muß aber nicht für selber geschriebene Destruktoren gelten. Zudem bezog sich mein Assigned(Objekt) ja eher auf die anderen Zugriffe auf solche Objektreferenzen die auch nil sein könnten.

@Michael:

Genau so habe ich das auch heraus gelesen und exakt deswegen bezweifle ich die Sinnhaftigkeit dieser Diskussion. Denn FreeAndNil() im Vergleich zu native .Free hat nichts mit den Fehlerursachen geschweige denn mit der Art&Weise der Programmierung noch Fehlersuche zu tun.

Die letzendliche Schlußfolgerung die ein unbedarfter Entwickler aus dieser Diskussion ziehen würde ist doch das man mit FreeAndNil() vorsichtig sein sollte, und das ist falsch, egal in welchem Kontext man FreeAndNil() statt .Free benutzt.

Falls es designtechnische Probleme mit .Free und FreeAndNil() gibt, die schlußendlich deren Existenzberechtigungen betreffen, dann kann das nur ein Problem der Feature der Programmiersprache sein. Dann wäre die Diskussion auch sinnvoll.

Die einzige Legitimation für FreeAndNil() statt einfachem .Free ist eben der Fakt das FreeAndNil() vor der Freigabe des Objektes die Objektreferenz auf nil zurücksetzt, neben dem Punkt das es ein Einzeiler im Source ist.

Gruß Hagen

PS: benutzt man FreeAndNil() statt .Free und hat den Designfehler gemacht das man im restlichen Program immer davon ausgeht das die Objektreferenz garnicht nil sein darf dann wird man Zugriffsschutzverletztungen bekommen die als Addresse 0x0000000?? angeben statt irgendeine Speicheradresse. Das ist für einen erfahrenen Entwickler der sofortige Hinweis das man irgendwo im Source auf ein "nil-Objekt" zugreift, ergo wesentlich konstruktiver für die Fehlerbeseitigung. Betrachtet man nun in diesem Zusammenhang noch den Fakt das die Speicherverwaltung freigegebene Speicherbereiche gleicher Größe sofort wieder verwendet dann kann es ohne FreeAndNil(), und späteren fehlerhaftem Zugriff auf ein nicht mehr existentes Speicherobjekt, dessen ehemaliger Speicherbereich nun schon längst wieder für ein anderes Objekt benutzt wird, noch zu viel wilderen und schwerer zu findenen Fehlern kommen. Ein weiteres Argument pro FreeAndNil().

mjustin 27. Feb 2010 13:15

Re: Frage zu FreeAndNil
 
Zitat:

Zitat von negaH
Die einzige Legitimation für FreeAndNil() statt einfachem .Free ist eben der Fakt das FreeAndNil() vor der Freigabe des Objektes die Objektreferenz auf nil zurücksetzt, neben dem Punkt das es ein Einzeiler im Source ist.

Warum soll eine Referenz auf ein Objektfeld *in einem Destruktor* auf nil gesetzt werden? Denn schliesslich soll danach kein Zugriff mehr auf das Feld erfolgen. Wenn dennoch eine Methode auf dieses Feld zugreifen will, dann ist das das im Thread gemeinte 'bad design'.

Wenn bei Verwendung von FreeAndNil() im Destruktor (nur darum geht es!) der nachfolgende Zugriff auf das freigegebene Feld an anderen Stellen mit "if Assigned()" abgesichert wird, wird keine AccessViolation ausgelöst. D.h. man verwendet zwei für sich genommen sinnvolle Konstrukte zur 'defensiven' Codierung, die zusammen dann aber einen Fehler verdecken.

negaH 27. Feb 2010 14:06

Re: Frage zu FreeAndNil
 
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.

Hawkeye219 27. Feb 2010 16:28

Re: Frage zu FreeAndNil
 
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

negaH 27. Feb 2010 22:56

Re: Frage zu FreeAndNil
 
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


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