Jak zwolnić zajętą pamięć TStringList

0

Nie wiem, czy to jest dobry pomysł. Globalnie zadeklarowałem sobie 2 listy
DaneLista, Wiersz: TStringList;
A teraz nie wiem, za bardzo w którym miejscu je zwolnić. Czy może ktoś zerknąć i naprowadzić mnie. Z góry serdecznie dziękuję. Poniżej listing:


program danetstringlist;
uses
  SysUtils, Classes;

var
  DaneLista, Wiersz: TStringList;
  NazwaPliku: string = 'dane.txt';

procedure OdczytajPlik(const NazwaPliku: string);
var
  id, Personalia, Wiek, Linia: string;
  i: Integer;
begin
  DaneLista := TStringList.Create;
  Wiersz := TStringList.Create;
  try
    if FileExists(NazwaPliku) then
    begin
      DaneLista.LoadFromFile(NazwaPliku);
      for i := 0 to DaneLista.Count - 1 do
      begin
        Linia := DaneLista[i];
        Wiersz.Delimiter := '|'; // Separator w pliku
        Wiersz.StrictDelimiter := True;
        Wiersz.DelimitedText := Linia;
        if Wiersz.Count = 3 then
        begin
          id := Wiersz[0];
          Personalia := Wiersz[1];
          Wiek := Wiersz[2];
          // Dla sprawdzenia wyświetl dane (na konsoli)
          Writeln('Id: ', id);
          Writeln('Imię i Nazwisko: ', Personalia);
          Writeln('Ilość Wiosen: ', Wiek);
        end;
      end;
    end
    else
      Writeln('Plik "', NazwaPliku, '" nie istnieje.');
  finally
    // jak zwalniam listy w tym miejscu dostaję ACCESS reading, 
    // że czytam po pamięci, która została zwolniona
    // DaneLista.Free;
    // Wiersz.Free;
  end;
end;

procedure WypiszStringList(const Dane: TStringList);
var
  i: Integer;
begin
  for i := 0 to Dane.Count - 1 do
    WriteLn(Dane[i]);
end;

begin
  OdczytajPlik(NazwaPliku);
  WypiszStringList(DaneLista);
  Readln;
end.


Dane w pliku dane.txt:

1|Adaś Pierwiosnek|9
2|Madzia Poziomka|11
3|Franek Motylek|8
4|Hania Malina|7

4
begin
  //tworzysz
  try
  OdczytajPlik(NazwaPliku); //z tego wywal tworzenie
  WypiszStringList(DaneLista);
  finally
    //zwalniasz
  end;
  Readln;
end.
2
program danetstringlist;
uses
  SysUtils, Classes;

var
  DaneLista: TStringList;
  NazwaPliku: string = 'dane.txt';

procedure OdczytajPlik(const NazwaPliku: string);
var
  id, Personalia, Wiek, Linia: string;
  i: Integer;
  Wiersz: TStringList; // LOKALNIE
begin
  DaneLista := TStringList.Create;
  Wiersz := TStringList.Create;
  try
    if FileExists(NazwaPliku) then
    begin
      DaneLista.LoadFromFile(NazwaPliku);
      for i := 0 to DaneLista.Count - 1 do
      begin
        Linia := DaneLista[i];
        Wiersz.Delimiter := '|'; // Separator w pliku
        Wiersz.StrictDelimiter := True;
        Wiersz.DelimitedText := Linia;
        if Wiersz.Count = 3 then
        begin
          id := Wiersz[0];
          Personalia := Wiersz[1];
          Wiek := Wiersz[2];
          // Dla sprawdzenia wyświetl dane (na konsoli)
          Writeln('Id: ', id);
          Writeln('Imię i Nazwisko: ', Personalia);
          Writeln('Ilość Wiosen: ', Wiek);
        end;
      end;
    end
    else
      Writeln('Plik "', NazwaPliku, '" nie istnieje.');
  finally
    Wiersz.Free;
  end;
end;

procedure WypiszStringList(const Dane: TStringList);
var
  i: Integer;
begin
  for i := 0 to Dane.Count - 1 do
    WriteLn(Dane[i]);
end;

begin
  OdczytajPlik(NazwaPliku);
  WypiszStringList(DaneLista);
  if Assigned(DaneLista) then
    DaneLista.Free;  //ZWALNIASZ PO UŻYCIU W WypiszStringList
  Readln;
end.

@kAzek mnie uprzedził. Lepiej jest raczej w jego propozycji tworzyć i zwalniać w jednym miejscu a nie między procedurami.

1

Zawsze lepiej zwolnić w bloku który wykona się zawsze nawet jeżeli wystąpi błąd. Poza tym tak jak napisałem wydaje mi się czytelniej skoro już używa tych list jako globalnych... przecież mógłby nie kombinować tylko drugą procedurę mógłby wywołać z poprzedniej i byłoby o wiele lepiej. Zresztą w ogóle inaczej napisać kod.

0

przecież mógłby nie kombinować tylko drugą procedurę mógłby wywołać z poprzedniej i byłoby o wiele lepiej. Zresztą w ogóle inaczej napisać kod

tutaj masz rację, trochę lawiruję, może nawet bardzo. Chciałem osiągnąć, że tylko raz wczytuje dane z pliku do 'TStringList' a później na niej sobie operować, tzn. dodawać, usuwać, modyfikować (na tej liście oczywiści). I dopiero przy wyjściu z programu zapisać zmiany z 'TStringList' do pliku 'dane.txt'
A tak przy stworzeniu nowej procedury DodajUzytkownika(const NazwaPliku: string); zapisuje dane bezpośrednio do pliku a nie do listy

// edit

temat do zamknięcia
na początku zrobiłem tak jak zasugerował : @kAzek & @Clarc

begin
  DaneLista := TStringList.Create;
  try
  OdczytajPlik(NazwaPliku);
  WypiszStringList(DaneLista);
  DodajStringList(DaneLista);
  WypiszStringList(DaneLista);
  ZapiszStringListDoPliku(DaneLista, NazwaPliku);
  finally
    FreeAndNil(DaneLista);
  end;
  Readln;

end.

ale finalnie stworzyłem class'e


type
  TDateStringList = class
  private
    DateList: TStringList;
    FileName: string;

// dodałem MENU:
procedure TDateStringList.DisplayMenu;

// i na koniec

var
  MyList: TDateStringList;
begin
  MyList := TDateStringList.Create('dane.txt');
  try
    MyList.ReadFile('dane.txt');
    MyList.DisplayMenu;
  finally
    MyList.Free;
  end;
end.

0

Jeśli to zmienne globalne to w użył bym sekcji initialization i finalization

var
  myStringlist :Tstringlist;
//// ...........
initialization
  myStringlist := Tstringlist.Create;
finalization
  myStringlist.Free;

PS.
Ale nie wiedzę żadnego powodu aby w Twoim kodzie musiały to być zmienne globalne ..
Utwórz listy w w procedurze OdczytajPlik (jako zmienne lokalne) i w niej je zwolnij

0

Jestem w trakcie przebudowania tego programu i myślę że idę w dobrym kierunku, ale możecie mnie poprawić,
ponieważ dopiero raczkuje w Free Pascal 'u. Poniżej listing co udało mi się do tej pory osiągnąć.


program datastringlistclass;

uses
  SysUtils, Classes;

type
  TDataStringList = class
  private
    DataList: TStringList;
    FileName: string;
  public
    constructor Create(const AFileName: string);
    destructor Destroy; override;
    procedure CheckExistFile;
    procedure ReadFile(const File_Name: string);
    procedure DisplayDataList;
    procedure AddRecordToDataList;
    procedure SaveToFile;
    procedure DisplayMenu;

  end;

  constructor TDataStringList.Create(const AFileName: string);
  begin
    DataList := TStringList.Create;
    FileName := AFileName;
  end;

  destructor TDataStringList.Destroy;
  begin
    DataList.Free;
    inherited;
  end;

procedure TDataStringList.CheckExistFile;
var
  DataFile: TextFile;
begin
  if not FileExists(FileName) then
  begin
    AssignFile(DataFile, FileName);
    Rewrite(DataFile);
    CloseFile(DataFile);
  end
end;

procedure TDataStringList.ReadFile(const File_Name: string);
var
  FileStream: TFileStream;
begin
  FileStream := TFileStream.Create(File_Name, fmOpenRead or fmShareDenyNone);
  try
    DataList.LoadFromStream(FileStream);
  finally
    FileStream.Free;
  end;
end;

procedure TDataStringList.DisplayDataList;
var
  i: Integer;
begin
  for i := 0 to DataList.Count - 1 do
    WriteLn(StringReplace(DataList[i], '|', ' ', [rfReplaceAll]));
end;

procedure TDataStringList.AddRecordToDataList;
var
  IDPerson, PersonalData, PersonsAge, NewPerson: string;
begin
  Write('Enter id: ');
  Readln(IDPerson);
  Write('Enter your first and last name: ');
  Readln(PersonalData);
  Write('Enter age: ');
  Readln(PersonsAge);
  NewPerson := IDPerson + '|' + PersonalData + '|' + PersonsAge;
  DataList.Add(NewPerson);
end;

procedure TDataStringList.SaveToFile;
begin
  DataList.SaveToFile(FileName);
end;

procedure TDataStringList.DisplayMenu;
var
  Choice: Integer;
begin
  repeat
    Writeln('Menu:');
    Writeln('1. View user');
    Writeln('2. Add user');
    Writeln('3. Save data to file');
    Writeln('4. End the program');
    Write('Select an option: ');
    Readln(Choice);

    case Choice of
      1:
        DisplayDataList;
      2:
        AddRecordToDataList;
      3:
        SaveToFile;
      4:
        Writeln('See you soon. Bye!');
    else
      Writeln('Incorrect selection. Try again.');
    end;
  until Choice = 4;
end;


var
  MyList: TDataStringList;
begin
  MyList := TDataStringList.Create('dane.txt');
  try
    MyList.CheckExistFile;
    MyList.ReadFile('dane.txt');
    MyList.DisplayMenu;
  finally
    MyList.Free;
  end;
end.

2

Tutaj masz klasę IStringList (TStringList oparta na interfejsie), w przypadku której nie musisz myśleć o zwalnianiu pamięci (wykona się "samo"): https://gitlab.com/cdbc-public/ibcstringlist
Więcej informacji w wątku: https://forum.lazarus.freepascal.org/index.php/topic,67154.msg516182.html#msg516182

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.