Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Lazarus, Problem mit TMemoryStream (https://www.delphipraxis.net/181644-lazarus-problem-mit-tmemorystream.html)

Gutelo 31. Aug 2014 23:08

Lazarus, Problem mit TMemoryStream
 
Hallo,

ich habe eine Frage bezueglich TMemoryStream unter Lazarus. Eine Instanz wurde erstellt mittels ms := TMemoryStream.Create;

Wenn ich zweimal hintereinander ms.Free aufrufe, dann haengt sich das Programm auf. Wieso ist das so? Zwar ist die Instanz nach dem ersten ms.Free nicht mehr vorhanden, aber so wie ich das verstanden habe sollte ein Object.Free auch dann arbeiten wenn der Pointer nil ist, d.h. vorher auf nil pruefen, oder nicht?

Gutelo

himitsu 31. Aug 2014 23:21

AW: Lazarus, Problem mit TMemoryStream
 
Weil man das nicht macht?
Ja macht es, also auf nil prüfen, aber Free setzt nichts auf nil.
Und nein, das ist nicht nur ein Lazarusproblem.

Wenn man nach dem Free nochmal auf den Instanzzeiger zugreifen will/muss, dann sollte man FreeAndNil verwenden.


Probleme haben viele, aber wo ist das "Free" im Titel?
PS: Das ist kein Problem vom Lazarus, sondern vom Compiler, also FreePascal, und das braucht man nicht in den Titel schreiben, sondern es gibt ein Präfix dafür in der Liste, oder du sgibst einfach das Lazarus mal in deinem Forenprofil an.

Gutelo 31. Aug 2014 23:34

AW: Lazarus, Problem mit TMemoryStream
 
Mein Hauptproblem ist, dass ich nicht weiss wie ich pruefen soll, ob die Instanz schon existiert.

Ich dachte eigentlich, dass Assigned(TObject) genau das macht, aber wenn ich ein "if Assigned(ms) then ms.Free;" durchfuehre dann haengt sich das Programm ebenfalls auf.

Gutelo

Sir Rufo 31. Aug 2014 23:41

AW: Lazarus, Problem mit TMemoryStream
 
Dann schau doch mal nach, was Assigned macht.

Delphi-Quellcode:
Assigned
prüft lediglich, ob die Referenz-Variable den Wert
Delphi-Quellcode:
nil
hat.
Aus, basta, fertig.
Delphi-Quellcode:
function Assigned( Obj : TObject ) : Boolen;
begin
  Result := Obj <> nil;
end;
Steht da etwas anderes drin als
Delphi-Quellcode:
nil
, dann liefert
Delphi-Quellcode:
Assigned
true zurück.

Es wird nicht geprüft, ob an der angegebenen Referenz eine gültige Instanz ist!

Setze einfach nach dem
Delphi-Quellcode:
Free
den Wert der Referenz-Variablen auf
Delphi-Quellcode:
nil
und schon kannst du problemlos mit
Delphi-Quellcode:
Assigned
prüfen.

Du kannst dafür auch
Delphi-Quellcode:
FreeAndNil
benutzen, das wie folgt arbeitet:
Delphi-Quellcode:
procedure FreeAndNil( var Obj : TObject );
begin
  Obj.Free;
  Obj := nil;
end;

Gutelo 31. Aug 2014 23:46

AW: Lazarus, Problem mit TMemoryStream
 
Was ist denn der richtige Weg zu pruefen ob sich hinter einer Variablen ein instantiiertes Objekt einer Klasse befindet?

if myObject is TObject then ... ?

Sir Rufo 31. Aug 2014 23:48

AW: Lazarus, Problem mit TMemoryStream
 
Zitat:

Zitat von Gutelo (Beitrag 1270457)
Was ist denn der richtige Weg zu pruefen ob sich hinter einer Variablen ein instantiiertes Objekt einer Klasse befindet?

if myObject is TObject then ... ?

Es gibt keinen Weg dafür! :roll:

himitsu 1. Sep 2014 00:53

AW: Lazarus, Problem mit TMemoryStream
 
Delphi-Quellcode:
x is TObject
fragt das, was in x verlinkt ist, ob es mindestens ein TObject ist und dabei wird nur das nil vorher abgefangen.
Wenn es auf "Schrott" zeigt, dann macht es das Selbe wie Assigned.

Der einzige richtige Weg ist, daß der Zeiger von DIR auf nil gesetzt wird, wenn du die Instanz freigibst. (entweder selber oder durch FreeAndNil)

Die Adresse auf einem Briefumschlag wird ja auch nicht automatisch leer, wenn du das Haus abreist. Außer du gehst selber mit'm Tintenkiller drüber, bzw. du beauftragst jemanden, der das für dich macht.

Medium 1. Sep 2014 00:57

AW: Lazarus, Problem mit TMemoryStream
 
Vielleicht noch ein Stück Hintergrundinfo: In der Variable steht ein Pointer auf ein Stück Speicher. Welchen wir OOPler gerne hochtrabend "Referenz" nennen, aber technisch ist es eben nur ein Pointer. Zumindest in den meisten nativ kompilierenden Umgebungen, wie Delphi und Lazarus für Windows.

An diesem Stück Speicher werden durch den Konstruktor eines Objektes ein Haufen Daten geschrieben die die eigentliche Instanz beschreiben, und der benutzte Speicher als "in Verwendung" markiert.

Das einzige was .Free() macht: Es nimmt die Markierung "in Verwendung" für das Stück Speicher wieder weg. Die Referenz, also unsere Variable, also den Pointer, lässt es unberührt. Es lässt sogar die Daten an diesem Stück Speicher unberührt! Heisst: Selbst nach einem .Free() kann dort noch die völlig intakte Instanz stehen! Eben so lange, bis der Speichermanager wieder für etwas anderes ein Stück Speicher braucht, und dieses als "frei" markierte Stück wird auserwählt UND danach auch wirklich mit anderen Daten beschrieben.

Folge: Selbst nach einem .Free() könnten selbst Low-Level Methoden, die nach einer Instanz-Artigen Struktur an der Stelle wo der Pointer hin zeigt suchen, können "false positive" Ergebnisse liefern. Auch eine Prüfung, ob der Speicher auf den gezeigt wird noch in Benutzung ist bringt keine gesicherten Ergebnisse, weil er könnte ja durchaus in der Zwischenzeit durch andere Instanzen oder Elemente belegt worden und somit in Benutzung sein - aber nicht eine gültige Instanz zu deinem erwarteten Objekt beinhalten.

Lösung: Du musst dir selber merken hinter welcher Referenz noch eine gültige Instanz liegt. Der einfachte Weg dafür ist der oben gezeigte: Die Referenz auf nil setzen.

Lustig wird sowas, wenn es mehrere Referenzen auf ein und die selbe Instanz geben könnte. Nil-Setzen einer Referenz macht dies natürlich nicht automatisch für alle möglichen anderen. Wenn so etwas vorkommt, gibt es diverse andere Strategien. Interfaces und Smart-Pointer sind zum Beispiel welche, und welche sich nachher eignet ist (wie immer) hoch individuell von der Aufgabe abhängig. (In deinem Fall reicht aber nil setzen alle Male aus.)

Dies betrifft praktisch alle Compiler die nativen Code erzeugen. Bei Sprachen wie Java oder Script-Sprachen kann es auch so sein, hier hätte man aber potenziell andere Möglichkeiten. (Die sicherlich von diversen auch genutzt werden, aber da habe ich wenig Detailerfahrung.)

Gutelo 1. Sep 2014 14:09

AW: Lazarus, Problem mit TMemoryStream
 
Danke fuer die Antworten.

Jetzt ist es mir klar geworden.

Im internet steht viel falsches darueber. Z.b. dass free automatisch auf nil setzt und dass viele Assigned(object) verwenden...

Gutelo

Medium 1. Sep 2014 17:12

AW: Lazarus, Problem mit TMemoryStream
 
Achtung: Es gibt in Delphi die Funktion FreeAndNil(MyObject). Die tut das!! Die Funktion sieht im Grunde so aus:
Delphi-Quellcode:
procedure FreeAndNil(var aObject: TObject);
var
  tmp: TObject;
begin
  tmp := aObject;
  if tmp <> nil then
    tmp.Free;
  aObject := nil;
end;
Wenn man etwas so frei gibt, dann kann man mit Assigned(MyObject) genau so arbeiten. Das geht aber nicht, wenn man nur MyObject.Free; benutzt. Schau nochmal genau nach.


Alle Zeitangaben in WEZ +1. Es ist jetzt 02:03 Uhr.
Seite 1 von 2  1 2      

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