![]() |
Delphi-Version: XE7
TObjectList.Free erzeugt AV
Ich stehe gerade etwas auf dem Schlauch und würde mich über eine Tipp freuen.
Ich habe eine TObjectList mit OwnObjects = True. In der Liste sind mehrere Objekte angefügt. Jetzt hab ich das Problem, dass ein Objekt an anderer Stelle schon frei gegeben wurde und ObjectList.Free eine Exception auslöst, da das eine Objekt nicht mehr existiert. Jetzt möchte ich vor dem ObjectList.Free prüfen, ob die Objekte in der Liste noch gültig sind und ungültige aus der Liste entfernen. Ich hab nur gerade ein Brett vorm Kopf, wie ich das abfrage. Wer kann helfen? |
AW: TObjectList.Free erzeugt AV
Wenn Du eine TObjectList hast, bei der die Liste der Eigentümer der Objekte ist, dann solltest Du auch das Freigeben der Objekte der Liste überlassen und die nicht selbst irgendwo freigeben ohne es der Liste mitzuteilen.
Der Schlüssel zum Erfolg liegt bei:
Delphi-Quellcode:
TObjectList.Remove(AObject: TObject);
|
AW: TObjectList.Free erzeugt AV
Das ist doch einfach: Es geht schlicht und ergreifend nicht.
Du hast die Verwaltung der Lifetime der Instanz an die Liste gegeben und nun schmeisst du es irgendwo aus dem Speicher. Das macht man nicht. Das ist so, als ob du mir etwas schenkst und dann mir wieder wegnimmst. Das gibt auch Mecker (mindestens). Nur das es bei Referenzen eben schlecht mit dem Kontrollieren aussieht, ob da an der Referenz noch etwas liegt. Denn der Speicher ist noch da (du klaust ja keinen RAM-Riegel aus dem System) und da kann auch noch irgendwas stehen. Es gibt also keine zuverlässige Möglichkeit zu prüfen ob unter der Referenz noch eine gültige Instanz zu finden ist. Mach es anders und alles wird gut. |
AW: TObjectList.Free erzeugt AV
OK, hatte gehofft, das man das prüfen kann. Dann mach ich anders, ist auch kein Problem.
Danke Euch für die Infos. |
AW: TObjectList.Free erzeugt AV
Wenn du anderswo ein entsprechendes eigenes Objekt zum Freigeben benötigst, kannst du ja eine Kopie erstellen. Alles andere wäre nicht sauber.
Eine mögliche Alternative wären Interfaces. |
AW: TObjectList.Free erzeugt AV
Wenn die Objekte nachfahren von TComponent sind, dann kann man deren Messagesystem benutzen und das Objekt sich selber aus der Liste löschen lassen, wenn es freigegeben wird.
Quasi eine TComponentList mit AutoRemove. Ab XE wäre das auch für alle anderen Nicht-TComponents relativ leicht möglich und davor bissl schwerer, aber ist nicht wirklich schön, sowas umzusetzen. |
AW: TObjectList.Free erzeugt AV
Ich glaube ich habe mich da etwas verrannt. Nach weiteren Überlegungen passen meine Idee und mein Ansatz dann doch nicht so ganz.
Ich arbeite schon mit einem TInterfacedObject, in dem die TObjectList enthalten ist. In einem Formular erzeuge ich im OnCreate noch andere benötigte Formulare, deren Referenz ich in der TObjectList ablege, damit die auf jeden Fall aufgeräumt werden, falls es mal krachen sollte. Die Unterformulare habe ich auf Position = poOwnerFormCenter eingestellt, und wollte die dann schön zentriert über dem Hauptformular anzeigen lassen. Das funktioniert nur, wenn die Unterformulare mit Owner = Hauptformular erzeugt werden. Wenn ich dann das Hauptformular schließe, dann werden durch den Owner auch die Unterformulare aufgeräumt und danach wird Interfaced ObjectList automatisch aufgeräumt und es gibt die Exception. Das zentrieren lasse ich jetzt, ich glaube, das war so ein Hirngespinst in das ich mich hineingesteigert habe und nicht wirklich brauche. Ich glaube, ich werde die Unterformulare auch erst dann erzeugen, wenn ich sie brauche. Sie werden nicht immer alle benötigt und sind nicht sehr umfangreich. Das Erzeugen wird nach Bedarf dann auch schnell gehen. |
AW: TObjectList.Free erzeugt AV
Warum sagst du denn nicht gleich, dass es sich um Nachfahren von
Delphi-Quellcode:
handelt, die du in der Liste speichern möchtest. :wall:
TComponent
Gerade die senden dir nach Anmeldung eine Nachricht, wenn die sich aus dem Speicher entfernen. ![]() ![]() Fliegt also so eine Instanz weg, ohne dass du die aus der Liste nimmst, dann wirf die einfach aus der Liste raus
Delphi-Quellcode:
CompList.OwnsObjects := False;
try CompList.Remove( AComponent ); finally CompList.OwnsObjects := True; end; |
AW: TObjectList.Free erzeugt AV
Zitat:
|
AW: TObjectList.Free erzeugt AV
Zitat:
|
AW: TObjectList.Free erzeugt AV
Sorry, ich bin nicht der 100%ige Delphi-Crack.
Wenn ich das jetzt richtig verstanden habe, dann müsste ich ja sinnvollerweise meine TObjectList informieren, wenn irgendein TComponent entfernt wird, damit es dann aus der Liste fliegt. Vielleicht ist es jetzt auch einfach zu spät. Ne, anders. Damit das mit FreeNotification funktioniert (TComponent), müsste ich das in mein Hauptformular implementieren, in dem ich die Unterformulare in die Liste stecke und dort dann im OnFreeNotification es aus der Liste entferne. (oder so ähnlich) Ja, ich glaube, ich habs so langsam verstanden. Das schaue ich mir aber morgen noch mal genau an. Jetzt ist erst mal Feierabend. |
AW: TObjectList.Free erzeugt AV
|
AW: TObjectList.Free erzeugt AV
Delphi-Quellcode:
Aber wenn man sowieso schon eine Owner- oder Parent-Beziehung hat, dann kann man sich eigentlich auch die zusätzliche Liste sparen, indem man die bereits existierenden Parent.Components- oder Parent.Controls-Listen mit benutzt.
type
TComponentList<T: TComponent> = class(TObjectList<T>) private type TNotify = class(TComponent) FParent: TComponentList<T>; procedure Notification(AComponent: TComponent; Operation: TOperation); override; end; private FNotify: TNotify; protected procedure Notify(const Value: T; Action: TCollectionNotification); override; public destructor Destroy; override; end; destructor TComponentList<T>.Destroy; begin FNotify.Free; inherited; end; procedure TComponentList<T>.Notify(const Value: T; Action: TCollectionNotification); begin inherited; if not Assigned(FNotify) then begin FNotify := TNotify.Create(nil); FNotify.FParent := Self; end; if Action = cnAdded then FNotify.FreeNotification(Value) else if not Contains(Value) then FNotify.RemoveFreeNotification(Value); end; procedure TComponentList<T>.TNotify.Notification(AComponent: TComponent; Operation: TOperation); begin inherited; if (Operation = opRemove) and not FParent.Contains(AComponent) then while FParent.Extract(AComponent) <> nil do ; end; Das Einzige, was ich mich bei Entwicklung dieser Liste gefragt hatte ..... wer zum Teufel war so wirklich saudämlich und hat vergessen das TList<T>.Create(), oder besser schon TEnumerable<T>.Create(), als virtual zu deklarieren, so daß man in Nachfahren ständig immer alle Konstrutoren überschreiben muß, wenn man da drin irgendwas initialisieren wöllte. :wall: Ach ja, und wer bei den FreeNotifications keine Registrierungszählung implementierte ... mehrere Verbindungen zwischen den selben Komponenten und man muß mit dem RemoveFreeNotification aufpassen. Genau deswegen setzte ich gern eine gekapselte Notify-Komponente ein, wenn ich nicht kontrollieren kann, ob zwischen referenzierenden Komponenten eventuell noch andere Referenzen, wie z.B. die Parent- oder Owner-Beziehungen existieren könnten. Alternativ könnte man nur registrieren (FreeNotification) und dürfte nie deregistrieren (RemoveFreeNotification), außer mal selber wird freigegeben, aber dann räumt sich das eh von selber auf. |
AW: TObjectList.Free erzeugt AV
|
AW: TObjectList.Free erzeugt AV
Zitat:
Ich überschreibe in so einem Fall meistens AfterConstruction und BeforeDestruction, was ja seit Delphi 4 für diesen Zweck existiert (wenn etwas nicht im Konstruktor gemacht werden kann)... // EDIT: Ja, genau... ich hatte die Antwortbox dank meines Sohnes wohl etwas zu lange offen. :D |
AW: TObjectList.Free erzeugt AV
Nja. Dafür gibt es so eine Methode weniger. :oops:
Aber mal ganz im Ernst, nun weil das nicht virtuell ist, haben die in TObjectList alle drei Konstructoren überschreiben müssen, obwohl es gereicht hätte, das Neue im nur Create() zu machen, da in den anderen zwei Konstructoren nichts anderes drin steht. |
AW: TObjectList.Free erzeugt AV
Hi zusammen
Hab ich das jetzt richtig gelesen? Du erzeugst Unterformulare und weist diesen deine Hauptform als Eigner Zu? Zitat:
Gruss Delbor |
AW: TObjectList.Free erzeugt AV
Wenn es nur um das Aufräumen geht, dann braucht man die Liste nicht, für andere Dinge schon ;)
|
AW: TObjectList.Free erzeugt AV
Hi Sir Rufo
Schon klar - eine einzige solche Liste kann mehrere dutzend(?) Zeilen Quelltext einsparen . Gruss Delbor |
AW: TObjectList.Free erzeugt AV
Moin,
neuer Tag neues Glück und die Sonne scheint, da kann es ja nur besser werden. Meine Idee hinter der ObjectList (Interface) ist, dass auf jeden Fall beim schließen des Hauptformulars alles aufgeräumt wird. Es sind nicht nur die Unterformulare, sondern auch andere Objekte wie Stringlisten, die in der ObjectList liegen. Falls mal, was auch immer, passiert, soll aufgeräumt werden. Die Anwender erzählen einem ja nicht immer alles, oder man selbst hat beim programmieren irgend einen Mist gebaut. Bisher war es kein Problem, die Objekte in die Liste zu schieben. Nun, jetzt wollte ich die schöne Funktion Position = poOwnerFormCenter für die Unterformulare verwenden, die jedoch mit einem Owner erzeugt werden müssen, damit das funktioniert. Das gab dann natürlich das Problem mit den 2 Ownern, einmal Hauptformular und einmal Liste. Deshalb die AV durch die Liste, weil das Hauptformular schon früher aufgeräumt hat. Nach dem ich mal ne Nacht drüber geschlafen habe, habe ich mich fürs Erste für folgende Lösung entschieden: Es bleibt erst mal alles in der Liste, und die räumt später auf. Und ich habe mir einen ClassHelper geschrieben:
Delphi-Quellcode:
Die Unterformulare rufe ich jetzt nicht mehr nur einfach mit ShowModal auf, sondern mit ShowModalCenter(Hauptform) und habe erst mal das erreicht was ich eigentlich wollte.
TShowModalCenter = class helper for TForm
procedure ShowModalCenter(ACenterForm: TForm); end; procedure TShowModalCenter.ShowModalCenter(ACenterForm: TForm); var X, Y: Integer; begin X := ((ACenterForm.Width - Width) div 2) + ACenterForm.Left; Y := ((ACenterForm.Height - Height) div 2) + ACenterForm.Top; if X < Screen.DesktopLeft then X := Screen.DesktopLeft; if Y < Screen.DesktopTop then Y := Screen.DesktopTop; SetBounds(X, Y, Width, Height); ShowModal; end; Eure Infos und Ideen muss ich mir aber auch noch mal durch den Kopf gehen lassen. Hab da im Moment nicht so viel Zeit, muss am Montag ne erste Version abliefern und hübsch kann ichs danach noch machen. |
AW: TObjectList.Free erzeugt AV
Das Aufräumen sollte immer dort passieren, wo das Objekt auch erstellt wurde. Wenn du eine TStringList im Konstruktor eines Objekts erstellst, sollte diese auch im Destruktor freigegeben werden. Sprich das Objekt, zu dem die Liste gehört, ist auch dafür verantwortlich. Natürlich musst das Objekt, das die Liste enthält, z.B. das Formular, auch freigegeben werden, damit das funktioniert. Und so geht es weiter bis zur obersten Ebene, bei der es kein übergeordnetes Objekt mehr gibt.
Aber das ist eine der wichtigsten Prinzipien der objektorientierten Programmierung. Dass du dich nicht in Verantwortlichkeiten anderer Objekte einmischst und so Kreuzbeziehungen usw. erzeugst. Wenn du eine einzige Liste führst, in das du diverse Objekte wirfst /werfen musst, nur damit diese freigegeben werden, ist das keine gute Struktur. |
AW: TObjectList.Free erzeugt AV
Musste den Code gerade noch mal etwas anpassen:
Delphi-Quellcode:
TShowModalCenter = class helper for TForm
function ShowModalCenter(ACenterForm: TForm): Integer; end; function TShowModalCenter.ShowModalCenter(ACenterForm: TForm): Integer; var X, Y: Integer; begin X := ((ACenterForm.Width - Width) div 2) + ACenterForm.Left; Y := ((ACenterForm.Height - Height) div 2) + ACenterForm.Top; if X < Screen.DesktopLeft then X := Screen.DesktopLeft; if Y < Screen.DesktopTop then Y := Screen.DesktopTop; SetBounds(X, Y, Width, Height); Result := ShowModal; end; |
AW: TObjectList.Free erzeugt AV
So eine ich beuge mal vor allem irgendwie vor Funktion ist absoluter Overkill und führt nur zu einem unsauberen Programmierstil. Wird ja immer alles so schön aufgeräumt am Schluss.
Gut im normalen Betrieb dauert es ca. 1h bis der Speicher überläuft, aber wir haben keinen MemLeak - dann ist doch alles gut, oder etwa nicht? Ein super Anti-Beispiel ist die FireMonkey ListBox aus XE7. Füll da mal so 10.000 Elemente rein (ist nur damit man auch was sieht) und dann gemütlich durch die Liste scrollen - rauf und runter. Toll, geht, aber was sagt der TaskManager zum Thema Speicherbedarf: Der wächst und wächst und wächst ... mit jedem Scrollen und hört einfach nicht auf. Es gibt aber keinen MemLeak, denn alles wird am Ende ja brav aufgeräumt. Dort jetzt den Fehler zu finden ist aufwändig. Bei einem MemLeak schaue ich mir die Instanzen an, wo es die meisten Leaks gibt und verfolge, wer die erzeugt und schwupps bin ich an der fehlerhaften Stelle. So habe ich die MemLeaks in dem RestResponseDatasetAdapter gefunden und ausgemerzt. Diese Art und Weise der Programmierung ist ähnlich schlimm, wie das paranoide Exception fangen. Lasst euch doch die Exceptions (so früh wie möglich) und die Leaks um die Ohren fliegen, dann wisst ihr wenigstens, wo es noch etwas zu tun gibt (sozusagen als automatischer ToDo-Eintrag). Falls es nicht bekannt sein sollte: Wenn sich die Anwendung beendet wird auch der gesamte Speicher der Anwendung freigegeben, ob Leak oder nicht, es bleibt nichts zurück. |
AW: TObjectList.Free erzeugt AV
@Sir Rufo:
Du hast schon recht, verstehe ich. Das steht auch noch das ein oder andere auf meiner ToDo Liste. Ich hab nur gerade das Problem, dass ich mit meinem Projekt etwas hinten dran bin und der Kunde schon jammert, dass er jetzt mal was sehen will. Auch wenns im Moment für den ein oder anderen QuickAndDirty ist, ich muss mal was fertig bekommen. Übrigens: dem Kunden ist es egal wie es programmiert ist, der will klicki bunti was schönes haben. Die andere Sache ist die, dass Du beim Testen niemals alles ausmerzen kannst. Man klickt halt anders rum, wie das ein Anwender tut. Hab ja schon MadExcept im Projekt und den Anwendern gesagt, die sollen mir den erzeugten Bericht senden, wenn eine Meldung kommt. Das machen aber vielleicht nur 30%, die anderen klicken immer weg und jammern hinterher, das Programm wäre ja Mist oder so ähnlich. Mit den Exception-Berichten hangele ich mich dann schon Stück für Stück an das Problem. try except nutze ich so gut wie gar nicht, ich bin schon bemüht ordentlich zu coden. Es soll Rund laufen, der Anwender soll zufrieden sein, dann ist auch der GF vom Kunden zufrieden. So ist der Plan. Auch wenn ich jetzt vielleicht wieder geschlagen werden: Ich nutze manchmal das System, was ![]() Ich finde das in manchen Fällen ganz Nett und hilfreich, verkürzt den Code, man kanns auch oft besser lesen, weils nicht so viel verschachtelt ist. Hilfmittel finde ich immer gut, sonst würde ich ja in Assembler programmieren. Meine ToDo Liste hat noch einige Punkte, aber eins nach dem anderen. Einer davon ist, dass ich die Exception Berichte im Hintergrund automatisch gesendet bekomme, dass ich dann handeln kann. |
AW: TObjectList.Free erzeugt AV
Zitat:
Mit Vista und Windows 7 hat sich da zwar einiges getan, aber ganz aus der Welt ist das Problem AFAIK dennoch nicht. |
AW: TObjectList.Free erzeugt AV
Zitat:
|
AW: TObjectList.Free erzeugt AV
Und selbst wenn Vieles inzwischen automatisch freigegeben wird.
Früher gab es in Windows nur einen großen Arbeitsspeicher, in dem alle Programme rumschrieben. Die Nachteile kennen wir:
Und nun hat man nur durch Aufräumen die Möglichkeit, später auch achzusehn, ob man irgendwo was vergessen hat und kann somit eventuell später Programmteile problemloser wiederverwenden. Das Resultat sieht man am Delphi, denn im Commandline-Compiler war es nicht schlimm, aber im Inline-Compiler sah man die Auswirkungen der Speicherlecks, denn auch nach dem Compilieren verstopfte es die IDE immer mehr. Und nur weil man jetzt denkt, daß etwas sowieso bis zum Programmende benötigt wird und man denkt "ach egal, Windows räumt schon auf", wer weiß was in Zukunft mal ist. Wenn schon, dann sollte man immer ordentlich arbeiten. (PS: siehe Signatur) |
AW: TObjectList.Free erzeugt AV
Momentchen a mal,
ich schreibe meine Programme immer mit
Delphi-Quellcode:
. Dann weiß ich, dass ich vergessen habe, irgendwo etwas korrekt freizugeben.
ReportMemoryLeaksOnShutdown := true;
Wenn ich jetzt aber irgendeine sonstwas Sammelliste im Programm habe, die alle Instanzen aufnimmt und dann am Ende garantiert freigibt, dann habe ich beim Beenden keine MemLeaks. Toll, ich bin ein Held! Oder doch nur der größte Tor von allen? Plötzlich melden sich auf einmal die Benutzer, weil nach 2-5h Betrieb der Anwendung ein Speicherfehler kommt: "Arbeitsspeicher voll!" Und warum? Denken, verstehen, handeln. Dann auf meine Signatur schauen und nicken. Und nochmal, ich verwende keine solchen Garbage-Collector-Listen (denn das sind die) (@himitsu schau mal in deine Signatur), sondern gebe den Speicher kontrolliert frei. Und falls ich es vergessen habe, dann knallt mit der FastMM das beim Beenden um die Ohren und das ist gut so. Wenn das nicht mehr kommt und ich am Arbeitsspeicherverbrauch nicht die Uhrzeit ablesen kann, erst dann habe ich es richtig gemacht. |
AW: TObjectList.Free erzeugt AV
Gibt es eine Möglichkeit in seinem eigenen Programm mitprotokollieren zu lassen, durch was Speicher verbraucht wird? Am besten wäre die Unit und das einzelne Objekt, die protokolliert wird.
Angenommen ich hab jetzt nicht sauber gearbeitet und während der Laufzeit wird der Speicherverbrauch immer höher, am Ende wirds aber abgeräumt und ich bekomme durch ReportMemoryLeaksOnShutdown := true; keine Meldung. Wie kann ich diesen Speicherverbrauch ermittelt, damit ich die Objekte entsprechend kontrolliert wieder freigeben kann? (Manchmal sieht man es ja nicht mehr, auch wenn man 100 mal über den Code schaut) Ich werde ja auch niemals das Programm 2 bis 5 Stunden am Stück testen und damit so arbeiten, wie es ein Benutzer macht. Trotzdem möchte ich sowas gerne herausfinden. |
AW: TObjectList.Free erzeugt AV
Zitat:
Delphi-Quellcode:
aus der FastMM4.pas von außen zugänglich machst.
CheckBlocksOnShutdown
Alternativ kannst du aber auch AQTime nutzen, das hat nen Memory Profiler. |
AW: TObjectList.Free erzeugt AV
Dafür gibt es auch spezielle Tools wie dieses:
![]() Solange du aber die Objekterzeugung und -freigabe nicht sauber umsetzt, wird dir das nicht furchtbar viel helfen fürchte ich. |
AW: TObjectList.Free erzeugt AV
Mit geht es hierbei um folgendes:
Zitat:
Das möchte ich gerne überprüfen, am besten für einen Tag beim Anwender protokollieren lassen und danach auswerten, damit ich gezielt im Code nachschauen kann. @Stevie und @jaenicke: Ich schaue mir die Infos an. PS: wie bekomme ich denn dieses CheckBlocksOnShutdown im Beitrag so angezeigt? |
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:58 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