![]() |
Objekte / Freigaben / Free / Nil
Hallo zusammen,
ich habe folgendes Beispiel und eigentlich zwei Fragen dazu:
Delphi-Quellcode:
Die Reihenfolge ist erst den Button1 zu betätigen, dann den Button2.
unit Unit4;
interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls; Type TFreeObjList = Class (TStringList) destructor Destroy; override; end; TMyOwnObject = class protected private Myfrm : TForm; function CheckA : boolean; public a : SmallInt; b : Double; c : TDateTime; d : AnsiString; e : String[40]; ObjInternList : TFreeObjList; function CheckB : Boolean; constructor Create; destructor Destroy; override; end; type TForm4 = class(TForm) Button1: TButton; Button2: TButton; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private { Private-Deklarationen } tmpObject : TMyOwnObject; MyObjList : TFreeObjList; public { Public-Deklarationen } end; var Form4: TForm4; implementation {$R *.dfm} {---------------------------------------------------------------------------} {------------ TFreeObjList- Stringliste mit Freigabe aller Objekte ---------} {---------------------------------------------------------------------------} destructor TFreeObjList.Destroy; Var i : Integer; TmpObj : TObject; begin for i:=0 to Count-1 do begin TmpObj := Objects[i]; if Assigned(TmpObj) then FreeAndNil(TmpObj); end; Inherited Destroy; end; {---------------------------------------------------------------------------} { TMyOwnObject } {---------------------------------------------------------------------------} function TMyOwnObject.CheckA: boolean; begin Result := A <> 0; end; {---------------------------------------------------------------------------} function TMyOwnObject.CheckB: Boolean; begin Result := B <> 0.0; end; {---------------------------------------------------------------------------} constructor TMyOwnObject.Create; begin Myfrm := TForm.Create(Application); ObjInternList := TFreeObjList.Create; a := 1; b := 2; c := Now; d := 'Hello'; e := 'World'; end; {---------------------------------------------------------------------------} destructor TMyOwnObject.Destroy; begin ObjInternList.Free; if Assigned(ObjInternList) then Sleep(0); inherited; end; {---------------------------------------------------------------------------} procedure TForm4.FormCreate(Sender: TObject); begin MyObjList := TFreeObjList.Create; end; {---------------------------------------------------------------------------} procedure TForm4.FormDestroy(Sender: TObject); var i : Integer; aObj : TMyOwnObject; begin MyObjList.Free; if Assigned(tmpObject) then begin if tmpObject.CheckA then Sleep(0); if Assigned(tmpObject.ObjInternList) then Sleep(0); end; if Assigned(MyObjList) then begin for i := 0 to MyObjList.Count - 1 do begin aObj := MyObjList.Objects[i] as TMyOwnObject; if Assigned(aObj) then begin if aObj.CheckA then Sleep(0); if aObj.CheckB then Sleep(0); end; end; end; end; {---------------------------------------------------------------------------} procedure TForm4.Button1Click(Sender: TObject); var aObj : TMyOwnObject; begin if Assigned(MyObjList) then begin aObj := TMyOwnObject.Create; MyObjList.AddObject(TimeToStr(aObj.c), aObj); end; end; {---------------------------------------------------------------------------} procedure TForm4.Button2Click(Sender: TObject); begin if Assigned(MyObjList) and (MyObjList.Count > 0) then begin tmpObject := MyObjList.Objects[0] as TMyOwnObject; if Assigned(tmpObject) then Sleep(0); end; end; {---------------------------------------------------------------------------} end. Da wird sich auf eine gloable Variable der Inhalt des ersten Objektes geholt. Wenn ich dann die Anwendung beende, passieren eigentlich zwei Dinge: 1) Das Objekt ist immernoch vorhanden und es kann drauf zugegriffen werden obwohl die Liste alle ihre Objekte durchgegangen ist und FreeAndNil gesagt hat
Delphi-Quellcode:
if Assigned(tmpObject) then
2) Vor dem Aufruf von "MyObjList.Free;" enthält das gloable Objekt noch den vollständigen Inhalt (inkl. des AnsiStrings "Hello"). Nach dem Aufruf von "MyObjList.Free;" ist bei dem globalen Objekt bis auf dem AnsiString noch alle vorhanden. Der AnsiString ist auf einmal nicht mehr "Hello" sondern ein Leerstring. Ich hätte doch dann jetzt erwartet, dass entweder alles weg ist (=Nil) oder noch alles da ist. Könnt ihr mich hier mal bitte aufklären? Umgebung = XE4 Gruß |
AW: Objekte / Freigaben / Free / Nil
Delphi-Quellcode:
Mal abgesehen davon, daß das if Assigned überflüssig ist, setzt FreeAndNil nur TmpObj auf nil, nicht aber Objects[I]. TmpObj := Objects[i]; if Assigned(TmpObj) then FreeAndNil(TmpObj); Strings haben eine implizite Referenzzählung und der Compiler behandelt sie bei Klassen und Records anders als andere Typen. |
AW: Objekte / Freigaben / Free / Nil
Ich würde im Destroy lieber rückwärts freigeben, also statt for i:=0 to Count-1 lieber for i:=Count-1 downto 0.
|
AW: Objekte / Freigaben / Free / Nil
Delphi-Quellcode:
Du kannst auch die TObjectList nutzen. Diese kann Objekte auch selbst freigeben.
TFreeObjList = Class (TStringList)
|
AW: Objekte / Freigaben / Free / Nil
@baumina:
Zitat:
@Uwe Raabe: Was müsste ich denn machen, um das Object[i] als solches auch freizugeben und auf Nil zu setzen? Gruß |
AW: Objekte / Freigaben / Free / Nil
Delphi-Quellcode:
Wobei mir nicht klar ist, wozu genilt werden soll, wenn die Liste danach eh futsch ist.
FreeAndNil(Objects[i]);
|
AW: Objekte / Freigaben / Free / Nil
Delphi-Quellcode:
weil sich u.U. durch Löschen der Index ändern könnte
Darf ich fragen warum?
|
AW: Objekte / Freigaben / Free / Nil
Zitat:
Delphi-Quellcode:
oder mit
for ... downto
Delphi-Quellcode:
while count > 0
|
AW: Objekte / Freigaben / Free / Nil
Zitat:
Edit: Eine andere Sache, die mir an dem Code hier auffällt, ist allerdings die völlige Sinnlosigkeit von FreeAndNil. Die Prozedur gibt dir hier nur falsche Sicherheit, denn alles was sie tut, ist, die lokale Variable auf nil zu setzen. In der Liste selbst ändert sich nichts. Ach das Prüfen mit Assigned ist daher völlig sinnlos und erschwert höchstens die Fehlersuche. |
AW: Objekte / Freigaben / Free / Nil
Seit wann schrumpft eine TStringlist, wenn ich deren Objekte freigebe?
|
AW: Objekte / Freigaben / Free / Nil
In dem Fall vielleicht nicht. Aber angenommen, man iteriert rein über den Index über eine Liste und
Delphi-Quellcode:
lässt nun alles nach Stelle 34 aufrücken? Dann doch wohl.
meineListe.EntferneEintragMitNummer(34)
|
AW: Objekte / Freigaben / Free / Nil
Zitat:
EDIT : Aber solltest du im Destroy nicht einen TypeCast auf TMyOwnObject machen? FreeAndNil(TMyOwnObject(Objects[i])) |
AW: Objekte / Freigaben / Free / Nil
Ok, danke für die Hinweise.
Mich interessiert natürlich jetzt noch das Thema mit dem FreeAndNil(tmpObj) und dem Object was noch später verfügbar ist. Bzw. Wie gibt man die Objecte innerhalb der Liste denn richtig frei? Gruß |
AW: Objekte / Freigaben / Free / Nil
Um den Knoten aufzulösen: Löschen mehrerer Elemente in einer Liste sollte man bei einer For-Schleife rückwärts machen, um Indexfehler zu vermeiden, da die Schleifenanzahl dort nur einmalig ermittelt wird. Alternativ kann man auch eine While-Schleife benutzen:
Delphi-Quellcode:
Beim Ändern von Elementen ist es im Normalfall egal, ob man das vorwärts oder rückwärts macht, es sei denn, die Liste löscht in bestimmten Fällen selbstständig Einträge. Sofern man sich nicht sicher ist: rückwärts durchlaufen, dann kann man nicht viel falsch machen.
while Liste.Count > 0 do
Liste.Delete(0); |
AW: Objekte / Freigaben / Free / Nil
Du kannst dir bei XE4 die Ableitung von TStringList sparen, wenn du deren property OwnsObjects auf true setzt. Damit gibt die Liste beim Löschen von Elementen und somit auch im eigenen Destroy auch die Objects frei.
|
AW: Objekte / Freigaben / Free / Nil
Zitat:
|
AW: Objekte / Freigaben / Free / Nil
Ich muss allerdings TStringList ableiten, da ich noch ein paar andere Funktionen nachgepflegt habe in meiner abgeleitetn TStringList.
Reicht es dann, wenn ich in meiner abgeleiteten Liste das OwnObjects auf True setze? Gruß |
AW: Objekte / Freigaben / Free / Nil
Zitat:
|
AW: Objekte / Freigaben / Free / Nil
Ich habe jetzt folgendes geändert aber das globale Objekt ist immernoch verfügbar:
Delphi-Quellcode:
Also hat sich eigentlich nichts dadurch geändert.
destructor TFreeObjList.Destroy;
Var i : Integer; TmpObj : TObject; begin {for i:=0 to Count-1 do begin TmpObj := Objects[i]; if Assigned(TmpObj) then FreeAndNil(TmpObj); end; } Inherited Destroy; end; procedure TForm4.FormCreate(Sender: TObject); begin MyObjList := TFreeObjList.Create; MyObjList.OwnsObjects := True; end; Was habe ich denn falsch gemacht bzw. falsch verstanden? Gruß |
AW: Objekte / Freigaben / Free / Nil
Zitat:
|
AW: Objekte / Freigaben / Free / Nil
Free bzw. Destroy gibt nur den Speicherbereich wieder frei – das sagt nur dem Speichermanager, dass er den Speicherplatz neu vergeben darf. Der Speicher wird aber nicht überschrieben – wozu auch, normalerweise sollte da ja niemand mehr drauf zugreifen.
Es kann also schon sein, dass nach einer Freigabe die Daten des Objektes noch an der Stelle im Speicher stehen, obwohl das Objekt nicht mehr existiert. Darauf kann man sich aber nicht verlassen. |
AW: Objekte / Freigaben / Free / Nil
Zitat:
Jede Klasse muss Destroy, wenn erforderlich, in geeigneter Weise überschrieben, um die Instanz vollständig freizugeben. |
AW: Objekte / Freigaben / Free / Nil
@Uwe: Ich habe ja nur den Quelltext an den beiden besagten Stellen angepasst. Siehe also mein Beispiel im ersten Post, dort steht ja im FormDestroy "MyObjList.Free;".
Trotzdem kann ich nach der besagten Änderung (OwnObjects=True) immernoch im FormDestroy nach dem "MyObjList.Free;" folgendes abfragen/aufrufen:
Delphi-Quellcode:
Das ist ja nur eine Minianwendung wo es recht übersichtlich ist. Wie kann man denn ganz sicher die Objekte freigeben und falls jemand anderes (vielleicht ein parallel laufender Thread) diese auf Gültigkeit abfragt auch wirklich feststellen, dass man noch drauf zu greifen kann?!
if Assigned(tmpObject) then
begin if tmpObject.CheckA then Sleep(0); if Assigned(tmpObject.ObjInternList) then Sleep(0); end; Gruß |
AW: Objekte / Freigaben / Free / Nil
Nebenbei hat eine TStringList bei aktuellen Delphiversionen die Eigenschaft OwnsObjects.
|
AW: Objekte / Freigaben / Free / Nil
Zitat:
Und Multi-Thread ist noch mal eine Sache für sich... |
AW: Objekte / Freigaben / Free / Nil
@jaenicke: Das war ja auch der Vorschlag von Uwe Raabe aber das Endergebnis ist ja das gleiche. Ich kann nicht erkennen, ob das Objekt noch da ist oder nicht.
Sowohl in meinem Beispiel in Post 1 als auch die Variante mit OwnsObjects wird mir mit ReportOnMemoryLeaks auch kein Speicherleck abgezeigt. Ist ja schön wenn das auch so ist aber ich verstehe nur nicht, warum ich auf alle Eigenschaften und Variablen des Objekts zugreifen kann obwohl es eigentlich freigegeben wurde. Gibt es keine Variante, dass Objekt so freizugeben, dass eine
Delphi-Quellcode:
Abfrage dann auch wirklich "False" liefert?
if Assigned
Gruß |
AW: Objekte / Freigaben / Free / Nil
Zitat:
|
AW: Objekte / Freigaben / Free / Nil
Das Problem ist nicht so einfach zu lösen.
Hier mal ein paar ältere Diskussionen dazu: ![]() ![]() |
AW: Objekte / Freigaben / Free / Nil
Ok danke für die Info. Wo wäre denn die richtige Stelle zum Nil setzen, wenn ich es selber machen muss @Uwe?
Gruß |
AW: Objekte / Freigaben / Free / Nil
Zitat:
Delphi-Quellcode:
procedure TForm4.FormDestroy(Sender: TObject);
begin tmpObject := nil; MyObjList.Free; MyObjList := nil; end; Die beiden if-Anweisungen würden demnach immer fehlschlagen und der dahinter liegende Code kann somit eliminiert werden. Solange die beiden Variablen nicht noch anderswo verwendet werden, braucht man sie dann aber auch nicht auf nil setzen. Damit reduziert sich das FormDestroy auf ein simples
Delphi-Quellcode:
MyObjList.Free;
Ich vermute aber, daß dein eigentlicher Code etwas ganz anderes macht. Dazu kann ich so natürlich nichts sagen. |
AW: Objekte / Freigaben / Free / Nil
Zitat:
Auf den diesjährigen Delphi-Tagen hatte ich dazu übrigens die Funktionsweise live im Debugger demonstriert. :-D Es funktioniert also - wenn auch (momentan) nur unter Windows, da dazu intern Assemblerfunktionen verwendet werden. |
AW: Objekte / Freigaben / Free / Nil
In den meisten Anwendungsfällen sind in so einem Fall aber eher Interfaces passend. Dann braucht man sich nicht darum zu kümmern und das Objekt wird freigegeben, wenn es von keiner Seite mehr benutzt wird. Umgekehrt hat man es aber auch überall noch, auch wenn es in der ursprünglichen Liste schon weg ist.
|
AW: Objekte / Freigaben / Free / Nil
Danke für die zahlreichen Hinweise.
Ich habe nur noch zwei letzte Fragen ;-) Aktuell habe ich die Variante mit den OwnsObjects aktiv und im FormDestroy rufe ich zur Zeit nur "FreeAndNil(MyObjList);" auf. Ich habe jetzt ja gelernt, dass dann das globale Objekt "tmpObject" noch vorhanden ist aber auf ein ungültigen Speicherbereich zeigt. 1) Woher weiß der Speichermanager eigentlich, dass diese Speicheradresse "ungültig" ist? Gibt es da irgendein Flag dafür? 2) Das das "if Assigned(tmpObject) then" weiterhin nach "FreeAndNil(MyObjList);" im FormDestroy funktioniert, habe ich verstanden aber warum crasht das Programm nicht, wenn ich dann auch auf die Inhalte und Funktionen von "tmpObject" zugreife nach der "if Assigned(tmpObject) then"-Abfrage? Gruß |
AW: Objekte / Freigaben / Free / Nil
Wenn Du ein Objekt freigibst wird nur der Speicherbereich als verfügbar definiert.
Wenn Du jetzt noch darauf zugreifst und der Speicher noch nicht überschreiben wurde, dann kann das noch gut gehen. Es kann aber auch sein, dass es in unvorhergesehener Form knallt, wenn die dort stehenden Binärdaten schon (teilweise) überschreiben wurde. In meinem aktuellen Framework habe ich übrigens folgende Lösung: In der Datanschicht sammle ich alle erzeugten Objekte in einer sortiereten Liste. Diese fordere ich dann zur Laufzeit anhand einer Id an. Durch Binary Search geht das wirklich schnell. Wurde ein Objekt bereits aufgelöst, gibt die Anfrage nil zurück. Man muss dann halt immer wieder die Objekte "abfordern". Die Objekte müssen dafür dann auch speziell angepasst sein. So funktioniert das gut, aber halt nur in einem festgelegten Rahmen.
Delphi-Quellcode:
function TssIO_Custom.GetObject(Id: TssId): TssObject;
var Index: Integer; ssObj: TssObject; begin Result := nil; ssObj := TssObject.Create(nil, Id, True); if ssObjectList.BinarySearch(ssObj, Index) then Result := ssObjectList[Index]; FreeAndNil(ssObj); end; procedure TssIO_Custom.RegisterObject(ssObj: TssObject); var Index: Integer; begin if not Assigned(ssObj) then Exit; if ssObj.HideMode then Exit; if not ssObjectList.BinarySearch(ssObj, Index) then ssObjectList.Insert(Index, ssObj); end; procedure TssIO_Custom.UnregisterObject(ssObj: TssObject); begin if not Assigned(ssObj) then Exit; if ssObj.HideMode then Exit; ssObjectList.Extract(ssObj); end; Eine Compilerlösung, Referenzen auf aufgelöste Objekte automatisch zu nilen ist wohl nicht in Aussicht. |
AW: Objekte / Freigaben / Free / Nil
Zitat:
Eine andere Möglichkeit wäre ein Kapselinterface, das einfach nur den Zeiger auf das Objekt enthält, der für die explizite Freigabe schlicht auf nil gesetzt wird. Dann kann man von überall über dieses Interface jederzeit prüfen, ob die Freigabe bereits erfolgt ist. Trotzdem halte ich echte Interfaces für die bessere Lösung für derlei Probleme, von ARC im mobile Compiler mal abgesehen. Solche Tricksereien um nur ja bei Objektreferenzen bleiben zu können, wenn sie eigentlich schlicht nicht zum aktuellen Anwendungsfall passen, empfinde ich nicht als saubere Lösung. |
AW: Objekte / Freigaben / Free / Nil
Es gibt natürlich Gründe dafür, dass es so ist wie es ist und es gibt Alternativen mit jeweils Vor- und Nachteilen.
Aber eines kann man sicher feststellen: Neulinge (im Delphi) werden immer davon ausgehen, dass Variablen, die auf ein Objekt zeigen, nach dessen Auflösung "leer" sind. Man kann mit der Zeit lernen, dass das nicht so ist. Aber dem Mißverständnis werden nahezu alle Delphi-Neulinge auf den Leim gehen. Insofern fände ich eine Compiler-Magie (oder wenigstens eine optionale Standard-Problemlösung) schon interessant. Alles andere sind mehr oder wenige aufwendige und mit Nachteilen verbundene Notlösungen. Aber ich sehe ein, dass eine automatische Lösung nicht unproblematisch zu realisieren wäre. |
AW: Objekte / Freigaben / Free / Nil
Grauenvoll, wenn der Compiler versucht, für mich mitzudenken. Das gab es mal bei VB und die Ergebnisse waren chaotisch. Wenn Variableninhalten sich auf einmal verändern, kann das nicht gut gehen.
Wenn mehr als ein Instanzenzeiger auf ein Objekt existiert, dann dürfte es sich um einen Designfehler handeln. Würde man einen Provider zwischenschalten, gäbe es die Probleme nicht: Der Provider meldet "is weg" oder instanziiert per lazy load eine neue Instanz. Daher ist es müßig (imho) sich darüber Gedanken zu machen, ob der Compiler diesen Lapsus reparieren soll. Na, ginge schon. Aber wenn man damit anfängt, dann müsste man auch gleich vergessene Freigaben, Ressourcenschutzblöcke usw. vom Compiler 'reparieren' lassen. Will hier keinem zu nahe treten, aber bitte bitte, nicht noch mehr Magic. |
AW: Objekte / Freigaben / Free / Nil
Zitat:
|
AW: Objekte / Freigaben / Free / Nil
Zitat:
|
AW: Objekte / Freigaben / Free / Nil
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:49 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz