Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   FreePascal (https://www.delphipraxis.net/74-freepascal/)
-   -   Brauche Hilfe bei der Fehlersuche. (https://www.delphipraxis.net/201789-brauche-hilfe-bei-der-fehlersuche.html)

Beach 26. Aug 2019 18:49

Brauche Hilfe bei der Fehlersuche.
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo zusammen,
habe für meine Arbeit ein kleines Programm geschrieben was auch prinzipiell macht was es soll.
Aber nach dem Aufruf einer auswertefunktion bekomme ich nach Beenden des Programms die Meldung im Anhang.
Dort heißt es das "1 unfreed Memory Block" Übrigbleibt.
Führe ich die Auswertung mit allen Daten durch, bleiben deutlich mehr (hatte schon 64) "unfreed Memory Blocks" über.

Wie gehe ich nun für die Fehlersuche vor?

In der Zeile 321 der mainform.pas wird eine StringList mittels Create erstellt. (und am Ende der Funktion mit FreeAndNil() wieder Freigegeben¿? )

Bin dankbar für jeden Tipp um meinen Fehler zu finden.

Klaus01 26. Aug 2019 18:58

AW: Brauche Hilfe bei der Fehlersuche.
 
.. kannst Du die Auswertefunktion hier zeigen?

Grüße
Klaus

zeras 26. Aug 2019 18:59

AW: Brauche Hilfe bei der Fehlersuche.
 
Zitat:

Zitat von Beach (Beitrag 1443357)

In der Zeile 321 der mainform.pas wird eine StringList mittels Create erstellt. (und am Ende der Funktion mit FreeAndNil() wieder Freigegeben¿? )

Bist du auch sicher, das du durch die Zeilen kommst, wo das Freigeben passiert?

Beach 26. Aug 2019 19:35

AW: Brauche Hilfe bei der Fehlersuche.
 
Die Zeile 321:
strListCalc := TStringList.Create;

Delphi-Quellcode:
procedure TForm1.alCalcExecute(Sender: TObject);
var
  StrListCalc: TStringList;

begin

  strListCalc := TStringList.Create;
  ZeissCFG := TClassZeissCFG.Create;
  StrListCalc := ZeissCFG.BuildNewFile(CFGValues, ValuesX, ValuesY, ValuesZ, DataLoaded);
  if CheckForSelectedFiles() then
  begin
    if SaveDialog1.Execute then
    begin
      if SaveDialog1.FileName <> '' then
      begin
        StrListCalc.SaveToFile(SaveDialog1.FileName);
        ShowMessage('Neue CFG Datei gespeichert!');
      end;
    end
    else
    begin
      ShowMessage('Datei NICHT gespeichert!');
    end;

  end;


  FreeAndNil(ZeissCFG);
  FreeAndNil(strListCalc);

end;

hoika 26. Aug 2019 20:02

AW: Brauche Hilfe bei der Fehlersuche.
 
Hallo,
kann es sein, dass
StrListCalc := ZeissCFG.BuildNewFile(CFGValues, ValuesX, ValuesY, ValuesZ, DataLoaded);

intern auch eine TStringList erzeugt und zurückgibt?

Um zu Deiner Frage zu kommen
Zitat:

Bin dankbar für jeden Tipp um meinen Fehler zu finden.
Code soweit ausklammern, bis kein Fehler erzeugt wird.
Dann schrittweise wieder rein, bis der Fehler wieder da ist.

Ich nehme mal folgendes an.
Fehler
Delphi-Quellcode:
  strListCalc := TStringList.Create;
  ZeissCFG := TClassZeissCFG.Create;
  StrListCalc := ZeissCFG.BuildNewFile(CFGValues, ValuesX, ValuesY, ValuesZ, DataLoaded);
  FreeAndNil(ZeissCFG);
  FreeAndNil(strListCalc);
kein Fehler
Delphi-Quellcode:
  strListCalc := TStringList.Create;
  ZeissCFG := TClassZeissCFG.Create;
  FreeAndNil(ZeissCFG);
  FreeAndNil(strListCalc);

Beach 26. Aug 2019 20:04

AW: Brauche Hilfe bei der Fehlersuche.
 
Ja. Die Funktion soll eine StringList zurückgeben.


Delphi-Quellcode:
function TClassZeissCFG.BuildNewFile(CFG: TCFGValues; XStrList: TRenishawValues;
  YStrList: TRenishawValues; ZStrList: TrenishawValues;
  DataLoaded: array of boolean): TStringList;

var
  i: integer;

const
  xpc = 1;
  ypc = 2;
  zpc = 3;

begin
  I := 0;
  Result := TStringList.Create;
  Result.Add(ArrayToStr(CFG.Text));
  Result.Add(ArrayToStr(CFG.Axis_x));
  Result.Add(ArrayToStr(CFG.Axis_y));
  Result.Add(ArrayToStr(CFG.Axis_Z));
  Result.Add(ArrayToStr(CFG.Tilt_y));
  Result.Add(ArrayToStr(CFG.Tilt_x));
  Result.Add(ArrayToStr(CFG.Tilt_const));


  // Wenn Renishaw Daten geladen wurden, werden diese genommen.
  // Ansonsten die Original Daten aus der CFG Datei


  // X-Achse

  if DataLoaded[xpc] then
  begin
    Result.Add('komp_x ' + IntToStr(XStrList.TargetCount + 2));
    Result.Add('-99999.999 0');
    for i := 0 to XStrList.TargetCount - 1 do
    begin
      Result.Add(XStrList.Targets[i] + ' ' + XStrList.Deviation[i][2]);
    end;
    Result.Add('99999.999 0');
  end
  else
  begin
    Result.Add('komp_x ' + IntToStr(cfg.Comp_count_x));
    for i := 0 to cfg.Comp_count_x - 1 do
    begin
      Result.Add(cfg.Comp_x[i][0] + ' ' + cfg.Comp_x[i][1]);
    end;
  end;


  // Y-Achse

  if DataLoaded[ypc] then
  begin
    Result.Add('komp_Y ' + IntToStr(YStrList.TargetCount + 2));
    Result.Add('-99999.999 0');
    for i := 0 to YStrList.TargetCount - 1 do
    begin
      Result.Add(YStrList.Targets[i] + ' ' + YStrList.Deviation[i][2]);
    end;
    Result.Add('99999.999 0');
  end
  else
  begin
    Result.Add('komp_y ' + IntToStr(cfg.Comp_count_y));

    for i := 0 to cfg.Comp_count_y - 1 do
    begin
      Result.Add(cfg.Comp_y[i][0] + ' ' + cfg.Comp_y[i][1]);
    end;
  end;


  // Z-Achse

  if DataLoaded[zpc] then
  begin
    Result.Add('komp_z ' + IntToStr(ZStrList.TargetCount + 2));
    Result.Add('-99999.999 0');
    for i := 0 to ZStrList.TargetCount - 1 do
    begin
      Result.Add(ZStrList.Targets[i] + ' ' + ZStrList.Deviation[i][2]);
    end;
    Result.Add('99999.999 0');
  end
  else
  begin
    Result.Add('komp_z ' + IntToStr(cfg.Comp_count_z));
    for i := 0 to cfg.Comp_count_Z - 1 do
    begin
      Result.Add(cfg.Comp_Z[i][0] + ' ' + cfg.Comp_Z[i][1]);
    end;
  end;
end;

HHennig 26. Aug 2019 20:05

AW: Brauche Hilfe bei der Fehlersuche.
 
try..finally könnte evtl. schon die Lösung sein:

Delphi-Quellcode:
procedure TForm1.alCalcExecute(Sender: TObject);
var
  StrListCalc: TStringList;

begin

  strListCalc := TStringList.Create;
  ZeissCFG := TClassZeissCFG.Create;
  try //<==[INDENT]
  StrListCalc := ZeissCFG.BuildNewFile(CFGValues, ValuesX, ValuesY, ValuesZ, DataLoaded);
  if CheckForSelectedFiles() then
  begin
    if SaveDialog1.Execute then
    begin
      if SaveDialog1.FileName <> '' then
      begin
        StrListCalc.SaveToFile(SaveDialog1.FileName);
        ShowMessage('Neue CFG Datei gespeichert!');
      end;
    end
    else
    begin
      ShowMessage('Datei NICHT gespeichert!');
    end;

  end;


[/INDENT] finally //<<==
    FreeAndNil(ZeissCFG);
    FreeAndNil(strListCalc);
  end;

end;
Ja, und inzwischen gab es schon einen Vorredner...geh seinem Verdacht ruhig auch nochmal auf den Grund!

HHennig 26. Aug 2019 20:07

AW: Brauche Hilfe bei der Fehlersuche.
 
Wenn deine Funktion eine Stringlist zurückgibt, ist das vorherige Erzeugen ausserhalb der Funktion definitiv das Problem; der angelegte Speicher wird nicht freigegeben da der Objektzeiger durch deine Funktionsrückgabe umgebogen wurde!

Beach 26. Aug 2019 20:15

AW: Brauche Hilfe bei der Fehlersuche.
 
Mit try...finally hatte ich schon probiert, ohne Erfolg.

Die Funktion BuildNewFile ist, laut ausklammern, der Übeltäter.

Zitat:

Zitat von HHennig (Beitrag 1443375)
Wenn deine Funktion eine Stringlist zurückgibt, ist das vorherige Erzeugen ausserhalb der Funktion definitiv das Problem; der angelegte Speicher wird nicht freigegeben da der Objektzeiger durch deine Funktionsrückgabe umgebogen wurde!

Dann habe ich da wohl ein Verständnisproblem.
Ich dachte, das ich, wenn ich eine StringList zurückliefere, diese auch in eine StringList Übergeben muss.

gubbe 26. Aug 2019 20:16

AW: Brauche Hilfe bei der Fehlersuche.
 
Das zeigt ganz gut, warum es keine so gute Idee ist, wenn eine Funktion eine Stringlist erstellt und zurückgibt. Es ist unklar, wessen Aufgabe es ist, sie wieder freizugeben. Eindeutiger ist es, eine Stringliste als Übergabeparameter der Funktion zu deklarieren.

Code:
StrListCalc := TStringList.Create;
ZeissCFG.BuildNewFile(StrListCalc, CFGValues, ValuesX, ValuesY, ValuesZ, DataLoaded);
...
StrListCalc.Free;
Die Funktion müsste natürlich entsprechend angepasst werden.

HHennig 26. Aug 2019 20:30

AW: Brauche Hilfe bei der Fehlersuche.
 
Zitat:

Dann habe ich da wohl ein Verständnisproblem.
Ich dachte, das ich, wenn ich eine StringList zurückliefere, diese auch in eine StringList Übergeben muss.
Das ist auch grundsätzlich korrekt, nur brauchst/darfst du die Stringlist nicht vorher erzeugen, denn das macht ja deine Funktion.

Wie aber gubbe schon sagte, sehr gefährlich das richtig frei zugeben!
Besser:

Delphi-Quellcode:
var
  MyStringList: TStringList;

begin
  MyStringList := TStringList.Create;
  try
    MeineProc(MyStringList);
    // Weitere Anweisungen mit der gefüllten Stringlist
    ...

  finally
    MyStringList.Free; // reicht aus
  end;
end;
so ungefähr

Ich sehe gerade, das hatte gubbe auch schon (besser) geschrieben...

Beach 26. Aug 2019 20:34

AW: Brauche Hilfe bei der Fehlersuche.
 
Zitat:

Zitat von gubbe (Beitrag 1443378)
[...]Eindeutiger ist es, eine Stringliste als Übergabeparameter der Funktion zu deklarieren.

Code:
StrListCalc := TStringList.Create;
ZeissCFG.BuildNewFile(StrListCalc, CFGValues, ValuesX, ValuesY, ValuesZ, DataLoaded);
...
StrListCalc.Free;
[...]

Ich glaube das war es jetzt.
Habe das so umgebaut und bei mehreren Tests war das Ergebnis jeweils "0 unfreed Memory Blocks"
Und was neues gelernt habe ich dabei auch.

Eine Frage noch.
Wann nutze ich FreeAndNil() und wann .Free ?

Vielen Dank an alle. Nun kann ich weiter basteln.
Die nächsten Fragen kommen bestimmt

HHennig 26. Aug 2019 20:41

AW: Brauche Hilfe bei der Fehlersuche.
 
nur Free machst du, wenn wie in meinem Beispiel die Objektvariable lokal angelegt wurde. Nach dem Verlassen des Blocks ist sie automatisch undefiniert (und ihr Speicher verloren, wenn nicht vorher freigegeben!)
FreeAndNil wenn nicht ganz klar ist, ob die Objektvariable evtl. nach der Freigabe noch irgendwo verwendet werden könnte. Wenn man dann vor jeder Verwendung auf nil abprüft, sollten ungültige Zugriffe (weil die Variable schon zerstört wurde) weitgehend ausgeschlossen sein.

haentschman 27. Aug 2019 05:28

AW: Brauche Hilfe bei der Fehlersuche.
 
Moin...8-)
Zitat:

Eindeutiger ist es, eine Stringliste als Übergabeparameter der Funktion zu deklarieren.
...nicht unbedingt. :zwinker: Aus dem Namen sollte hervorgehen, das ein Objekt (in diesen Falle eine TStringList) erzeugt wird.
Bsp:
Delphi-Quellcode:
function CreateAndFillBlubbList(Bla: Integer): TStringList;

Luckie 27. Aug 2019 12:11

AW: Brauche Hilfe bei der Fehlersuche.
 
Zitat:

FreeAndNil wenn nicht ganz klar ist, ob die Objektvariable evtl. nach der Freigabe noch irgendwo verwendet werden könnte
Und wenn du an diesen Punkt bist, weißt du, dass du schlechten Code geschrieben hast. Wenn man sauber programmiert, ist FreeAndNil nicht nötig.

Rolf Frei 27. Aug 2019 16:18

AW: Brauche Hilfe bei der Fehlersuche.
 
Zitat:

Zitat von Beach (Beitrag 1443386)
Ich glaube das war es jetzt.
Habe das so umgebaut und bei mehreren Tests war das Ergebnis jeweils "0 unfreed Memory Blocks"
Und was neues gelernt habe ich dabei auch.

Noch um es dir klar zu machen wieso das nicht ging:

Eine Delphiklassenvariable ist im Prinzip ein Pointer auf einen Speicherbereich, in dem die Klasse erzeugt wurde. Wenn du nun also in deiner Funktion eine weitere Stringlist erstellst, wird diese einen anderen Speicherbereich haben, also ein neuer Pointer darauf. Durch die Rückgabe dieser erzeugten Klasse, gibst du also nicht den Speicherinhalt zurück, sondern den Pointer auf diesen Speicher. Damit ist deine ausserhalb der Funktion erstellte Variable nicht mehr ein Pointer auf diese Klasse, sondern hat nun den Pointer aus der Funktion zugewiesen. Sie sieht also nun das Objekt der in der Funktion erzeugten Stringlist. Der vorherige Pointer dieser Variable ist nun verloren und der allozierte Speicher kann nicht mehr freigegeben werden, da nun dein Free die in der Funktion erzeugte Klasse freigibt und nicht die erste.

Gewöhn dir aber bitte an, wann immer du ein Objekt erstellst, dieses mit einem try/finally zu schützen. Nur so wird deine Klasse sauber freigeben, wenn innerhalb des Create und Free eine Exception auftritt.

Delphi-Quellcode:
x := TMeinObjekt.Create;
try
  x.TueIrgendwas;
finally
  x.Free; // Kannst auch FreeAndNil verwenden. Ist im prinzip das Selbe, nur dass danach x = nil ist.
end;

hoika 27. Aug 2019 16:28

AW: Brauche Hilfe bei der Fehlersuche.
 
Hallo,
Zitat:

Kannst auch FreeAndNil verwenden. Ist im prinzip das Selbe, nur dass danach x = nil ist.
Nicht ganz.

Folgendes geht:
FreeAndNil(x);
FreeAndNil(x);

Folgendes nicht:
x.Free;
x.Free;

FreeAndNil prüft, ob die Variable nicht bereits schon nil ist.

gubbe 27. Aug 2019 16:33

AW: Brauche Hilfe bei der Fehlersuche.
 
Zitat:

Zitat von hoika (Beitrag 1443524)

FreeAndNil prüft, ob die Variable nicht bereits schon nil ist.

Das macht Free auch.


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