Italian community of Lazarus and Free Pascal

Programmazione => Generale => Topic aperto da: luigi67 - Marzo 05, 2014, 01:28:43 pm

Titolo: Come assicurarsi del corretto rilascio delle risorse
Inserito da: luigi67 - Marzo 05, 2014, 01:28:43 pm
Salve
Come da titolo, come faccio ad assicurarmi che tutte le risorse "figlie" sono correttamente rilasciate prima della della distruzione di una risorsa genitore?.
Mi spiego meglio ho una finestra principale in cui c'è una griglia collegata ad un database oggetto TDataModule che contiene svariati oggetti

TIBConnection, TIBDatabase, TFBLDatabase,
TSQLTransaction, TIBTransaction, TFBLTransaction
 TSQLQuery, TIBTable, TFBLMetadata,TRxMemoryData e
TDatasource

Se faccio un inserimento di dati e poi chiudo la finestra, rilasciando con free la relativa risorsa mi da un errore che non riesco ad intercettare con il debug, ma che mi fa pensare ad un incorretto rilascio delle risorse del datamodule, dato che se non faccio alcun inserimento, apro e richiudo l'applicazione, le risorse sono rilasciate correttamente. Se utilizzo un pulsante con "dati.Free;" e poi chiudo l'applicazione si chiude correttamente, se invece inserisco nell'evento "on close" dell'applicazione  sempre dati.Free; mi da errore.
Vorrei sapere se c'0è modo di conoscere in fase di chiusura se ci sono ancora risorse "figlie" allocate e rilasciare di conseguenza.
Grazie
Luigi
Titolo: Re:Come assicurarsi del corretto rilascio delle risorse
Inserito da: luigi67 - Marzo 05, 2014, 01:43:33 pm
mi correggo anche nel caso di utilizzo di dati.free, al momento della chiusura mi da l'errore, quindi non credo sia legato al datamodule ma ad un errato rilascio delle risorse l'errore che mi dà è

classe 'External: SIGSEGV'.
Titolo: Re:Come assicurarsi del corretto rilascio delle risorse
Inserito da: xinyiman - Marzo 05, 2014, 03:00:10 pm
Riesci a darci un sorgente di prova? Detto così potrebbe essere qualunque cosa.
Titolo: Re:Come assicurarsi del corretto rilascio delle risorse
Inserito da: luigi67 - Marzo 05, 2014, 03:18:22 pm
ci provo, vi do la parte che mi pare incriminata,

Questo è l'avvio del programma


program trattamenti;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Interfaces, // this includes the LCL widgetset
  Forms, rxnew, sdflaz, rx, lazdbexport, ibexpress, zcomponent, zvdatetimectrls,
  fortes324forlaz, appforms, principale, ModuloDati, memo, repprod, reptrattam,
  repvisteid, repviste, messaggioiniz,FileUtil,comune,sysutils,XMLConf,LCLType;

{$R *.res}

begin
RequireDerivedFormResource := True;
Application.Initialize;
WorkDir:= ExtractFilePath(Application.ExeName);
comune.XMLConfigAppl:=TXMLConfig.Create(XMLConfigAppl);
comune.XMLConfigAppl.Filename:=WorkDir+fileImpost;
comune.DatabaseName:=XMLConfigAppl.GetValue('Dati/DatabaseName','DataBaseName');
if FileExistsUTF8(string (comune.DatabaseName)) then
   begin
   Application.CreateForm(TDati, Dati);
   Application.CreateForm(TFPrincipale, FPrincipale);
    end
  else
      begin
      MsgIni:=TMsgIni.Create(Application);
      mresult:=MsgIni.ShowModal;
      MsgIni.Free;
      if ((mresult=IDOK) or (mresult=IDYES)) then
         begin
        Application.CreateForm(TDati, Dati);
        Application.CreateForm(TFPrincipale, FPrincipale);
         end
      else
         begin
         Application.Terminate;
         end;
      end;
  Application.Run;
 end.
                           

e questa è la chiusura della finestra principale


procedure TFPrincipale.FormClose(Sender: TObject; var CloseAction: TCloseAction
  );
begin
if (DBGrPiante.DataSource.DataSet.State in dsEditModes) then
   begin
   CloseAction:=Chiusura(DBGrPiante.DataSource.DataSet);
   end
else if  (DBGrTipo.DataSource.DataSet.State in dsEditModes) then
    begin
    CloseAction:=Chiusura(DBGrTipo.DataSource.DataSet);
    end
else if (DBGrProdotti.DataSource.DataSet.State in dsEditModes)then
     begin
     CloseAction:=Chiusura(DBGrProdotti.DataSource.DataSet);
     end
else if (DBGrTrattamenti.DataSource.DataSet.State in dsEditModes) then
     begin
     CloseAction:=Chiusura(DBGrTrattamenti.DataSource.DataSet);
     end
else
    begin
    CloseAction:=caFree;
    end;
   if CloseAction=caFree then
      begin
      SalvaImpostazioni;
      Dati.TRPrincipale.Commit;
      end;
end;

procedure TFPrincipale.SalvaImpostazioni;
begin
comune.XMLConfigAppl.SetValue('Application/WorkDir',WorkDir);
comune.XMLConfigAppl.Flush;
comune.XMLConfigAppl.Free;
end;


ho provato ad aprire e chiudere da console senza fare alcuna operazione e il risultato della console è il seguente

Heap dump by heaptrc unit
283876 memory blocks allocated : 45059990/46007960
283876 memory blocks freed     : 45059990/46007960
0 unfreed memory blocks : 0
True heap size : 1114112
True free heap : 1114112


mentre se faccio qualche operazione sul database e e poi chiudo

Heap dump by heaptrc unit
359575 memory blocks allocated : 49749830/50878464
359567 memory blocks freed     : 49749678/50878296
8 unfreed memory blocks : 152
True heap size : 1245184
True free heap : 1244416
Should be : 1244504
Call trace for block $B5965340 size 16
An unhandled exception occurred at $005EA636:
EAccessViolation:
  $005EA636



ho Voluto lanciarla da console con il debug abilitato per capire se è un problema dell'ide e a quanto sembra no è un errore di rilasio delle risorse. Boh!

Titolo: Re:Come assicurarsi del corretto rilascio delle risorse
Inserito da: xinyiman - Marzo 05, 2014, 03:27:52 pm
Intendo proprio tutto il sorgente zippato. Così lo debuggo e vedo se riesco ad aiutarti.
Titolo: Re:Come assicurarsi del corretto rilascio delle risorse
Inserito da: luigi67 - Marzo 05, 2014, 03:45:28 pm
scusa, ma come faccio ad aggiungere il file zip?
Titolo: Re:Come assicurarsi del corretto rilascio delle risorse
Inserito da: nomorelogic - Marzo 05, 2014, 05:47:19 pm
quando rispondi, appena sotto il "Memo" in cui scrivi, c'è un link che devi espandere: "Allegati ed altre opzioni"
Titolo: Re:Come assicurarsi del corretto rilascio delle risorse
Inserito da: luigi67 - Marzo 05, 2014, 06:01:31 pm
Allora credo di aver scoperto il problema è dovuto al fatto che uso una lista a puntatori e non rilascio la memoria.
Ho un oggetto di questo tipo


PProdotti=^TProdotti;
 TProdotti=record
   pianta:string;
   IDprodotto:string;
   quantita:single;
   next:PProdotti;


allocato dinamicamente con  con



function TFPrincipale.CaricaRecTrattamenti(var numpiante:integer):PProdotti;
var
ProdPrimo,Prodotti,ProdCorrente,ProdPrecedente:PProdotti;
PPiante:PPianta;
begin
   try
   dati.TTempTrattamenti.First;
   Prodotti:=CaricaProdotti;
   ProdPrimo:=Prodotti;
   PPiante:=CaricaPiante;
   dati.TTempTrattamenti.DisableControls;
   ProdPrecedente:=Nil;
   new(ProdCorrente);
   numpiante:=0;
   Result:=ProdCorrente;
   while PPiante<>nil do
      begin
      inc(numpiante,1);
      while Prodotti<>Nil do
         begin
         if ProdPrecedente=nil then
            begin
            ProdCorrente^.pianta:=PPiante^.pianta;
            ProdCorrente^.IDprodotto:=Prodotti^.IDprodotto;
            ProdCorrente^.quantita:=Prodotti^.quantita;
            ProdPrecedente:=ProdCorrente;
            end
       else
           begin
           ProdPrecedente:=ProdCorrente;
            new(ProdCorrente);
            ProdCorrente^.pianta:=PPiante^.pianta;
            ProdCorrente^.IDprodotto:=Prodotti^.IDprodotto;
            ProdCorrente^.quantita:=Prodotti^.quantita;
            ProdPrecedente^.next:=ProdCorrente;
            end;
         ProdCorrente^.next:=Nil;
         Prodotti:=Prodotti^.next;
      end;
   Prodotti:=ProdPrimo;
   PPiante:=PPiante^.next;
   end;
   finally
   dati.TTempTrattamenti.EnableControls;
   end;
end;
                                       


ma come vedi nella procedura Non c'è il rilascio della memoria!! :-\
per rilasciare la memoria come faccio ripercorro la lista e vado rilasciando la memoria o basta fare un dispose di ProdPrimo?
Titolo: Re:Come assicurarsi del corretto rilascio delle risorse
Inserito da: nomorelogic - Marzo 05, 2014, 06:14:13 pm
ciao, l'argomento è già stato affrontato, dai un'occhiata a questo 3D
http://www.lazaruspascal.it/index.php?topic=1244.msg6743#msg6743 (http://www.lazaruspascal.it/index.php?topic=1244.msg6743#msg6743)
Titolo: Re:Come assicurarsi del corretto rilascio delle risorse
Inserito da: luigi67 - Marzo 05, 2014, 08:46:25 pm
Allora ho due alternative o vado cancellando nodo per noto oppure uso un'allocazione iniziale della dimensione che mi serve conoscendo il numero dei record con getmen
.........
numrecord:=contarecord
GetMem(recPointer, numrecord * SizeOf(TRecord));
 e alla fine
freemen(recPointer, numrecord * SizeOf(TRecord));
 rifacendomi all'esempio
http://www.delphibasics.co.uk/RTL.asp?Name=GetMem&ExpandCode1=Yes#Ex1

e così non ci dovrebbe essere più la necessità di usare le liste o sbaglio?
Altra domanda dato che uso una funzione che restituisce un puntatore devo rilasciare la memoria usata dalle variabili di tipo PProdotti all'interno della funzione e poi riguardo alla funzione stessa che restituisce un result di tipo  PProdotti? una volta rilasciata la memoria della lista si dovrebbe rilasciare anche quella della funzione giusto?
Ciao
Luigi
Titolo: Re:Come assicurarsi del corretto rilascio delle risorse
Inserito da: Stilgar - Marzo 06, 2014, 09:38:01 am
O Mamma.
Ma usare gli oggetti?
In questo modo non è l'utilizzatore che gestisce la memoria ma direttamente l'oggetto.
Mi piego.
".Create"
".Destroy"
servono a questo.
Se hai una catena di oggetti, basta che in ".Destroy" metti il codice che elimina il prossimo nodo.
Codice: [Seleziona]
if assigned(next) then
  FreeAndNil(FNext);
due righe semplici semplici.
Poi saranno affari del gestore dello heap a rilasciare correttamente la ram ;)


Codice: [Seleziona]
TProdotto = class(TObject)
protected
   FPianta:string;
   IDProdotto:string;
   FQuantita:single;
   FNext:TProdotto;
public
  Constructor Create;
  Destructor Destroy; override;
published
  property Pianta: String;
  property Quantita : Single;
  Next : TProdotto;
end;

....

Constructor TProdotto.Create;
Begin
   FPianta:='';
   IDProdotto:='';
   FQuantita:=0.0;
   FNext:=nil;
End;

Destructor TProdotto.Destroy;
Begin
   FPianta:='';
   IDProdotto:='';
   FQuantita:=0.0;
  if Assigned(FNext) then FreeAndNil(FNext);
  inherited Destroy;
End;

....

La butto lì senza nemmeno passare per l'ide ;)

Stilgar[/code]
Titolo: Re:Come assicurarsi del corretto rilascio delle risorse
Inserito da: nomorelogic - Marzo 06, 2014, 10:17:20 am
premetto che ci sono mooooolte alternative all'uso delle liste di puntatori, alternative che gestiscono bene la memoria senza impazzire (ad esempio open array e le liste di oggetti come suggerito da Stilgar)


Allora ho due alternative o vado cancellando nodo per noto oppure uso un'allocazione iniziale della dimensione che mi serve conoscendo il numero dei record con getmen

esatto
(oppure usi gli open array come detto sopra ;) )

Altra domanda dato che uso una funzione che restituisce un puntatore devo rilasciare la memoria usata dalle variabili di tipo PProdotti all'interno della funzione e poi riguardo alla funzione stessa che restituisce un result di tipo  PProdotti?

se ti riferisci a
ProdPrimo,Prodotti,ProdCorrente,ProdPrecedente:PProdotti;PPiante:PPianta;

lo spazio per le variabili dichiarate nella sezione "var" sono allocate in memoria al lancio della procedura/funzione e sono deallocate immediatamente dopo il termine di questa
e questo a prescindere dal loro contenuto

quindi: no



una volta rilasciata la memoria della lista si dovrebbe rilasciare anche quella della funzione giusto?

in che senso?
la funzione quando termina su autorilascia
Titolo: Re:Come assicurarsi del corretto rilascio delle risorse
Inserito da: luigi67 - Marzo 06, 2014, 11:19:59 am
Ragazzi siete veramente in gamba! Credo che adotterò la soluzione di Stilgar, mi sembra la più elegante e assolve al mio scopo che non è sviluppare un'applicazione. Si non è questo  il mio vero scopo. Sto sviluppando questa applicazione per rimparare a programmare.
In pratica sto riprendendo dopo anni di inattività, sono rimasto a Delphi 7, completamente dimenticato.
Ho scoperto Lazarus per caso e vi devo dire è stato amore a prima vista sopratutto perché open e supportato da tanto materiale che si trova in rete e forum come questo!
Io di mestiere sono un Network manager e il mio sistema operativo principe è Linux nella versione Centos  che conosco abbastanza bene, di conseguenza lazarus! su linux!
Ok adesso per puro esercizio accademico come dicevo prima volevo allocare la memoria che mi serve e po de-allocarla questo un frammento di codice ripreso dalla pagina delphi



 in cosa sbaglio? in pratica alloco la quantità di memoria necessaria e
function TFPrincipale.CaricaProdotti(var numprod:integer):PProdotti;
var
i:integer;
mempos:string;
prod:PProdotti;
begin
try
i:=1;
dati.TTempTrattProdotti.Filtered:=true;
dati.TTempTrattProdotti.First;
Result:=Nil;
numprod:= THackGrid(DBGRTrattProdotti).VisibleRowCount;
Getmem(prod,numprod*SizeOf(TProdotti) );//alloco la memoria necessaria
for i := 1 to numprod do
begin
prod^. IDprodot:=dati.TTempTrattProdotti.FieldByName('Prodotto').AsString; //quì mi da errore
dati.TTempTrattProdotti.Next;
Inc(Result);
end;
........



ho notato che in delphi l'assegnazione è differente


begin
  // Allocate storage for three records
  // Note : It is better to use New for this
  // It is used here for illustration purposes only
  GetMem(recPointer, 3 * SizeOf(TRecord));

  // Fill out these 3 records with values
  recPointer.name := 'Brian';  //io invece devo usare "^." perché?
  recPointer.age  := 23;

  Inc(recPointer);
  recPointer.name := 'Jim';
  recPointer.age  := 55;

  Inc(recPointer);
  recPointer.name := 'Sally';
  recPointer.age  := 38;

  // Now display these values
  Dec(recPointer, 2);
  ShowMessageFmt('%s is %d',[recPointer.name, recPointer.age]);
  Inc(recPointer);
  ShowMessageFmt('%s is %d',[recPointer.name, recPointer.age]);
  Inc(recPointer);
  ShowMessageFmt('%s is %d',[recPointer.name, recPointer.age]);
end;
Titolo: Re:Come assicurarsi del corretto rilascio delle risorse
Inserito da: luigi67 - Marzo 06, 2014, 12:25:44 pm
Allora rifacendomi all'esempio di Stilgar ho creato per maggiore chiarezza una unit in cui creo due oggetti: eccovi di seguito il codice

unit oggetti;

{$mode objfpc}{$H+}{$M+}

interface

uses
   Classes, SysUtils;
type

{ TPianta }

TPianta=class(TObject)
private
 procedure SetNomePianta(AValue: String);
protected
FNomePianta:string;
Fnext:TPianta;
public
  Constructor Create;
  Destructor Destroy; override;
published
  Next : TPianta;
  property NomePianta: String read FNomePianta write SetNomePianta;
   end;

{ TProdotto }

TProdotto = class(TObject)
private
   procedure SetIDProdotto(AValue: String);
   procedure SetNomePianta(AValue: String);
   procedure SetQuantita(AValue: Single);
protected
   FNomePianta:string;
   FIDProdotto:string;
   FQuantita:single;
   FNext:TProdotto;
public
  Constructor Create;
  Destructor Destroy; override;
published
  Next : TProdotto;
  property NomePianta: String read FNomePianta write SetNomePianta;
  property IDProdotto: String read FIDProdotto write SetIDProdotto;
  property Quantita : Single read FQuantita write SetQuantita;

 end;


implementation

{ TPianta }

procedure TPianta.SetNomePianta(AValue: String);
begin
   if NomePianta=AValue then Exit;
   FNomePianta:=AValue;
end;


constructor TPianta.Create;
begin
 FNomePianta:='';
end;

destructor TPianta.Destroy;
begin
  FNomePianta:='';
  if Assigned(FNext) then FreeAndNil(FNext);
  inherited Destroy;
end;


{ TProdotto}
procedure TProdotto.SetNomePianta(AValue: String);
begin
   if FNomePianta=AValue then Exit;
   FNomePianta:=AValue;
end;

procedure TProdotto.SetIDProdotto(AValue: String);
begin
   if FIDProdotto=AValue then Exit;
   FIDProdotto:=AValue;
end;



procedure TProdotto.SetQuantita(AValue: Single);
begin
   if FQuantita=AValue then Exit;
   FQuantita:=AValue;
end;


constructor TProdotto.Create;
Begin
   FNomePianta:='';
   IDProdotto:='';
   FQuantita:=0.0;
   FNext:=nil;
End;

destructor TProdotto.Destroy;
Begin
   FNomePianta:='';
   IDProdotto:='';
   FQuantita:=0.0;
  if Assigned(FNext) then FreeAndNil(FNext);
  inherited Destroy;
End;
end.


Vi chiedo come faccio a caricare i dati su qesti oggetti, caipisco che devo usare le properti per assegnare i valori alle singole voci es:


PProdotto: TProdotto
PProdotto:=TProdotto.create(self);
PProdotto.IDProdotto:='123';
......
per il record successivi al primo come faccio, devo richiamare pprodotto.next.idprodotto? Mi manca un passaggio
Grazie
Luigi
Titolo: Re:Come assicurarsi del corretto rilascio delle risorse
Inserito da: Stilgar - Marzo 06, 2014, 12:49:34 pm
Codice: [Seleziona]
TProdotto.create(self);
Qui non compila.
Per il resto sei sulla strada giusto :D

Questa catena la devi gestire alla vecchia maniera. Tieni solo il primo oggetto referenziato. Poi sono affari suoi gestire il resto della catena.
Se vuoi farlo più ficaccione il codice :
Codice: [Seleziona]
property  Next : TProdotto read getNext write setNext;

Codice: [Seleziona]
procedure TProdotto.setNext(value: TProdotto);
begin
  if Assigned(fNext) then fNext.Next := Value
  else
    FNext := Value;
end;

In questo modo ottieni "gratis" la ricerca della coda. Metti l'ultimo next alla fine della catena.

Aggiungi che sfrutti lo stack per la ricerca della posizione. Meno cicli for e while ;)
Solo un if e un call.

Stilgar
Titolo: Re:Come assicurarsi del corretto rilascio delle risorse
Inserito da: luigi67 - Marzo 06, 2014, 01:03:17 pm
si scusa era senza parametro il create.
mi manca il getnext?
Titolo: Re:Come assicurarsi del corretto rilascio delle risorse
Inserito da: Stilgar - Marzo 06, 2014, 01:18:49 pm
Codice: [Seleziona]
function TProdotto.getNext : TProdotto;
begin
  result := FNext;
end;

Non ricordo la shortcut .. ma c'è il modo di dire a Lazarus di fare il lavoro noioso al posto mio...
Crtl+O?
Possibbole?

Stilgar :p
OT: Inizio a sentire la primavera .. mo so affari vostri :D
Titolo: Re:Come assicurarsi del corretto rilascio delle risorse
Inserito da: luigi67 - Marzo 06, 2014, 01:23:37 pm


allora questo è il codice modificato della unit oggetti


unit oggetti;

{$mode objfpc}{$H+}{$M+}

interface

uses
   Classes, SysUtils;
type

{ TPianta }

TPianta=class(TObject)
private
 procedure SetNomePianta(AValue: String);
protected
FNomePianta:string;
Fnext:TPianta;
public
  Constructor Create;
  Destructor Destroy; override;
published
  Next : TPianta;
  property NomePianta: String read FNomePianta write SetNomePianta;
   end;

{ TProdotto }

TProdotto = class(TObject)
private
   procedure SetIDProdotto(AValue: String);
   procedure SetNext(AValue: Tprodotto);
   procedure SetNomePianta(AValue: String);
   procedure SetQuantita(AValue: Single);
 protected
   FNomePianta:string;
   FIDProdotto:string;
   FQuantita:single;
   FNext:TProdotto;
public
  Constructor Create;
  Destructor Destroy; override;

published

  property NomePianta: String read FNomePianta write SetNomePianta;
  property IDProdotto: String read FIDProdotto write SetIDProdotto;
  property Quantita : Single read FQuantita write SetQuantita;
  property Next:Tprodotto read FNext write SetNext;
 end;


implementation

{ TPianta }

procedure TPianta.SetNomePianta(AValue: String);
begin
   if NomePianta=AValue then Exit;
   FNomePianta:=AValue;
end;


constructor TPianta.Create;
begin
 FNomePianta:='';
end;

destructor TPianta.Destroy;
begin
  FNomePianta:='';
  if Assigned(FNext) then FreeAndNil(FNext);
  inherited Destroy;
end;


{ TProdotto}
procedure TProdotto.SetNomePianta(AValue: String);
begin
   if FNomePianta=AValue then Exit;
   FNomePianta:=AValue;
end;

procedure TProdotto.SetIDProdotto(AValue: String);
begin
   if FIDProdotto=AValue then Exit;
   FIDProdotto:=AValue;
end;

procedure TProdotto.SetNext(AValue: Tprodotto);
begin
   if Assigned(fNext) then
   begin
   fNext.Next := AValue;
   end
    else
    begin
      FNext := AValue
    end;
end;



procedure TProdotto.SetQuantita(AValue: Single);
begin
   if FQuantita=AValue then Exit;
   FQuantita:=AValue;
end;


constructor TProdotto.Create;
Begin
   FNomePianta:='';
   IDProdotto:='';
   FQuantita:=0.0;
   FNext:=nil;
End;

destructor TProdotto.Destroy;
Begin
   FNomePianta:='';
   IDProdotto:='';
   FQuantita:=0.0;
  if Assigned(FNext) then FreeAndNil(FNext);
  inherited Destroy;
End;
end.




e questo un frammento della  funzione che la richiama, chiaramente manca la creazione di next infatti alla seconda assegnazione fatta dal ciclo for mi da errore




function TFPrincipale.CaricaProdotti(var numprod:integer):TProdotto;
var
//corrente,precedente:PProdotti;
i:integer;
mempos:string;
prod:TProdotto;
begin
try
i:=1;
Result:=Nil;
prod:=TProdotto.Create;
numprod:= THackGrid(DBGRTrattProdotti).VisibleRowCount;

for i := 1 to numprod do al 2° ciclo errore su
begin
prod.IDProdotto:=dati.TTempTrattProdotti.FieldByName('Prodotto').AsString; 2° ciclo errore su
prod.Quantita:=dati.TTempTrattProdotti.FieldByName('FDQuantita').AsFloat;
prod:=prod.Next;
end;


......
Titolo: Re:Come assicurarsi del corretto rilascio delle risorse
Inserito da: Stilgar - Marzo 06, 2014, 01:35:39 pm
Se non ti avesse dato errore mi sari incazzato con il compilatore... :D
Scommetto che si incazza perchè trova il valore NEXT non valorizzato.... o meglio valorizzato a pene segugiorum.
Codice: [Seleziona]
constructor TPianta.Create;
begin
 FNomePianta:='';
   FIDProdotto:='';
   FQuantita:=0.0.;
   FNext:=NIL;
 // Non facciamo gli scansafatiche, impostiamo TUTTI i volori di default a mano. ;)
end;
//...
var TestaProdotti, ProdottoCursore : TProdotto;
//...
for i := 1 to numprod do //al 2° ciclo errore su
begin
  ProdottoCursore.IDProdotto:=dati.TTempTrattProdotti.FieldByName('Prodotto').AsString; 2° ciclo errore su
  ProdottoCursore.Quantita:=dati.TTempTrattProdotti.FieldByName('FDQuantita').AsFloat;
  ProdottoCursore := TProdotto.Create;  // <----- SE NON LI CREI NON LI USI :D SE LI USI E NON LI CREI ... Buuum
  TestaProdotti.Next := ProdottoCursore;
end;
Perdonato solo perchè stai riprendendo in mano la programmazione ;)
Stilgar

OT: Come cantava?
Maledetta primavera?
Titolo: Re:Come assicurarsi del corretto rilascio delle risorse
Inserito da: luigi67 - Marzo 06, 2014, 09:14:57 pm
Grazie Stilgar
Adesso non posso provare la modifica, domani la prima cosa lo modifico. Ti devo dire che ci ero arrivato e ti invierò una copia di quello che ho fatto senza la tua modifica. Un'ultima cosa alla fine però perdo il riferimento alla lista; in pratica arrivo in fondo e basta... ho inserito una proprietà chiamata "first" che come da nome dovrebbe  mantenere il riferimento all'inizio della lista, ma poi mi sono ricordato che l'oggetto viene ricreato ogni volta di conseguenza... mi viene in mente "static" come reminescenza...mah
Titolo: Re:Come assicurarsi del corretto rilascio delle risorse
Inserito da: luigi67 - Marzo 07, 2014, 09:46:10 am
allora di seguito la unit oggetto modificata. Mi manca come dicevo un passaggio e cioè il puntatore al primo elemento. In pratica riempo la lista  e poi perdo l riferimento.
Codice: [Seleziona]

unit oggetti;

{$mode objfpc}{$H+}{$M+}

interface

uses
   Classes, SysUtils;
type

{ TPianta }

TPianta=class(TObject)
private
 procedure SetNomePianta(AValue: String);
protected
FNomePianta:string;
Fnext:TPianta;
public
  Constructor Create;
  Destructor Destroy; override;
published
  Next : TPianta;
  property NomePianta: String read FNomePianta write SetNomePianta;
   end;

{ TProdotto }
TProdotto = class(TObject)
private
   function GetFirst: TPRodotto;
   procedure SetIDProdotto(AValue: String);
   procedure SetNext(AValue: Tprodotto);
   procedure SetNomePianta(AValue: String);
   procedure SetQuantita(AValue: Single);
   function  GetNext:TProdotto;
 protected
   FNomePianta:string;
   FIDProdotto:string;
   FQuantita:single;
   FNext:TProdotto;
   FFirst:TProdotto;
public
  Constructor Create;
  Destructor Destroy; override;

published

  property NomePianta: String read FNomePianta write SetNomePianta;
  property IDProdotto: String read FIDProdotto write SetIDProdotto;
  property Quantita : Single read FQuantita write SetQuantita;
  property Next:Tprodotto read GetNext write SetNext;
  property First:TPRodotto read GetFirst;
 end;


implementation

{ TPianta }

procedure TPianta.SetNomePianta(AValue: String);
begin
   if NomePianta=AValue then Exit;
   FNomePianta:=AValue;
end;


constructor TPianta.Create;
begin
 FNomePianta:='';
end;

destructor TPianta.Destroy;
begin
  FNomePianta:='';
  if Assigned(FNext) then FreeAndNil(FNext);
  inherited Destroy;
end;


{ TProdotto}
procedure TProdotto.SetNomePianta(AValue: String);
begin
   if FNomePianta=AValue then Exit;
   FNomePianta:=AValue;
end;

function TProdotto.GetFirst: TPRodotto;
begin
metodo vuoto dato che non riueco a capire come restituire il primo elemento della lista
end;

procedure TProdotto.SetIDProdotto(AValue: String);
begin
   if FIDProdotto=AValue then Exit;
   FIDProdotto:=AValue;
end;

procedure TProdotto.SetNext(AValue: Tprodotto);
begin
 if FNext=AValue then Exit;
 FNext:=AValue;
end;




procedure TProdotto.SetQuantita(AValue: Single);
begin
   if FQuantita=AValue then Exit;
   FQuantita:=AValue;
end;

function TProdotto.GetNext: TProdotto;
begin
FNext:=TProdotto.Create;
result := FNext;
end;


constructor TProdotto.Create;
Begin
   FNomePianta:='';
   IDProdotto:='';
   FQuantita:=0.0;
     FNext:=nil;
End;

destructor TProdotto.Destroy;
Begin
   FNomePianta:='';
   IDProdotto:='';
   FQuantita:=0.0;
  if Assigned(FNext) then FreeAndNil(FNext);
  inherited Destroy;
End;

end.

qui invece come viene richiamato l'oggetto


Codice: [Seleziona]
ProdottoCursore:=TProdotto.Create;
numprod:= THackGrid(DBGRTrattProdotti).VisibleRowCount;
//Getmem(prod,numprod*SizeOf(TProdotto) );
//inc(prod);
for i := 1 to numprod do
begin
ProdottoCursore.IDProdotto:=dati.TTempTrattProdotti.FieldByName('Prodotto').AsString;
ProdottoCursore.Quantita:=dati.TTempTrattProdotti.FieldByName('FDQuantita').AsFloat;
dati.TTempTrattProdotti.Next;
ProdottoCursore := TProdotto.Create;
TestaProdotti.Next := ProdottoCursore;


Sicuramente la soluzione è lì ma io per poca conoscenza di questi meccanismi non la vedo
Titolo: Re:Come assicurarsi del corretto rilascio delle risorse
Inserito da: Stilgar - Marzo 07, 2014, 09:59:56 am
Per vitare la perdita della lista, piccolo trucchetto.
Fai una funzione al posto di una procedura.
Quando la richiami, crea e popola la lista.
L'utilizzatore poi ha in mano la testa della lista e ne farà quello che deve :D

Codice: [Seleziona]
Function TContenitore.carica: TProdotto;
var
  ProdottoCursore : TProdotto;
  numprod: integer;
begin
  ProdottoCursore := TProdotto.Create;
  Result :=  ProdottoCursore;
  ProdottoCursore:=TProdotto.Create;
  numprod:= THackGrid(DBGRTrattProdotti).VisibleRowCount;
  for i := 1 to numprod do
  begin
    ProdottoCursore.IDProdotto:=dati.TTempTrattProdotti.FieldByName('Prodotto').AsString;
    ProdottoCursore.Quantita:=dati.TTempTrattProdotti.FieldByName('FDQuantita').AsFloat;
    dati.TTempTrattProdotti.Next;
    ProdottoCursore := TProdotto.Create;
    Result.Next := ProdottoCursore;
  End;
End;


Altra considerazione.
Semplifichi la lettura, ma da designer ho sempre qualche dubbio ;)
Questo oggetto è un oggetto di business, quindi potresti anche caricarlo della lettura e scrittuta sul dataset, a livello di competenze.

Quindi potresti mettere in piedi 2 metodilli carini.
Uno di lettura e uno di scrittura.
Codice: [Seleziona]
procedure TProdotto.LoadFromDataSet(const ds : TDataSet);
begin
  FIDProdotto := ds.FieldByName('Prodotto').asString;
  FQuantita:= ds.FieldByName('FDQuantita').asFloat;
end;


procedure TProdotto.SaveToDataSet(const ds : TDataSet);
begin
  try
  ds.edit;
  ds.FieldByName('Prodotto').asString := FIDProdotto ;
  ds.FieldByName('FDQuantita').asFloat := FQuantita;
  ds.post;
  except
  on E : Exception do
     raise EProdottoPersistenceServiceException .Create("Errore in salvataggio dei dati", e);
  end;
end;


Type
  EProdottoPersistenceServiceException = class(Exception)
   protected
     FCause : Exception;
   public
      constructor Create(messageValue: String; causeValue : Exception=null);
      property Cause : Exception read FCause;
  end;


      constructor EProdottoPersistenceServiceException .Create(messageValue: String; causeValue : Exception);
      begin
         inherited Create(messageValue);
         FCause := causeValue;
      end;

Il getFirst nei nodi...a cosa ti serve?
Se ti serve a puntare alla testa della lista ...
Consumi ram per nulla.
Se vuole essere il nodo precedente, bene.
Non è una lista "semplice" ma "doppia" e mi torna ;)

Stilgar
Titolo: Re:Come assicurarsi del corretto rilascio delle risorse
Inserito da: Stilgar - Marzo 07, 2014, 10:00:36 am
PS:
Tutto il codice lo scrivo di getto.
Non lo passo al compilatore ;)


Stilgar
Titolo: Re:Come assicurarsi del corretto rilascio delle risorse
Inserito da: luigi67 - Marzo 07, 2014, 11:06:01 am
Nente la lista si perde comunque. Se per maggiore precisione in ptatica richiamo la funzikone da un pulsante questo è il codice

Codice: [Seleziona]
procedure TFPrincipale.SpeedButton1Click(Sender: TObject);
var
recprod:TProdotto;
s:string;
begin
recprod:=CaricaProdotti;
s:=recprod.Next.IDProdotto;

la variabile s  vuota

Ti allego nuovamente tutto il codice
 :'(
Codice: [Seleziona]
(*-----------------UNIT OGGETTI -----------*)

unit oggetti;

{$mode objfpc}{$H+}{$M+}

interface

uses
   Classes, SysUtils;
type

{ TPianta }

TPianta=class(TObject)
private
 procedure SetNomePianta(AValue: String);
protected
FNomePianta:string;
Fnext:TPianta;
public
  Constructor Create;
  Destructor Destroy; override;
published
  Next : TPianta;
  property NomePianta: String read FNomePianta write SetNomePianta;
   end;

{ TProdotto }
TProdotto = class(TObject)
private
   function GetFirst: TPRodotto;
   procedure SetIDProdotto(AValue: String);
   procedure SetNext(AValue: Tprodotto);
   procedure SetNomePianta(AValue: String);
   procedure SetQuantita(AValue: Single);
   function  GetNext:TProdotto;
 protected
   FNomePianta:string;
   FIDProdotto:string;
   FQuantita:single;
   FNext:TProdotto;
   FFirst:TProdotto;
public
  Constructor Create;
  Destructor Destroy; override;

published

  property NomePianta: String read FNomePianta write SetNomePianta;
  property IDProdotto: String read FIDProdotto write SetIDProdotto;
  property Quantita : Single read FQuantita write SetQuantita;
  property Next:Tprodotto read GetNext write SetNext;
  property First:TPRodotto read GetFirst;
 end;


implementation

{ TPianta }

procedure TPianta.SetNomePianta(AValue: String);
begin
   if NomePianta=AValue then Exit;
   FNomePianta:=AValue;
end;


constructor TPianta.Create;
begin
 FNomePianta:='';
end;

destructor TPianta.Destroy;
begin
  FNomePianta:='';
  if Assigned(FNext) then FreeAndNil(FNext);
  inherited Destroy;
end;


{ TProdotto}
procedure TProdotto.SetNomePianta(AValue: String);
begin
   if FNomePianta=AValue then Exit;
   FNomePianta:=AValue;
end;

function TProdotto.GetFirst: TPRodotto;
begin

end;

procedure TProdotto.SetIDProdotto(AValue: String);
begin
   if FIDProdotto=AValue then Exit;
   FIDProdotto:=AValue;
end;

procedure TProdotto.SetNext(AValue: Tprodotto);
begin
 if FNext=AValue then Exit;
 FNext:=AValue;
end;




procedure TProdotto.SetQuantita(AValue: Single);
begin
   if FQuantita=AValue then Exit;
   FQuantita:=AValue;
end;

function TProdotto.GetNext: TProdotto;
begin
FNext:=TProdotto.Create;
result := FNext;
end;


constructor TProdotto.Create;
Begin
   FNomePianta:='';
   IDProdotto:='';
   FQuantita:=0.0;
   FNext:=nil;
End;

destructor TProdotto.Destroy;
Begin
   FNomePianta:='';
   IDProdotto:='';
   FQuantita:=0.0;
  if Assigned(FNext) then FreeAndNil(FNext);
  inherited Destroy;
End;

end.



(*--------------------------Funzione Carica Prodotti------------------------*)



function TFPrincipale.CaricaProdotti:TProdotto;
var
mempos,s:string;
ProdottoCursore : TProdotto;
begin
   try
   ProdottoCursore := TProdotto.Create;
   Result :=  ProdottoCursore;
   dati.TTempTrattProdotti.DisableControls;
   dati.TTempTrattProdotti.Filtered:=true;
   dati.TTempTrattProdotti.First;
  while not dati.TTempTrattProdotti.eof do
       begin
        ProdottoCursore.IDProdotto:=dati.TTempTrattProdotti.FieldByName('Prodotto').AsString;
        ProdottoCursore.Quantita:=dati.TTempTrattProdotti.FieldByName('FDQuantita').AsFloat;
        dati.TTempTrattProdotti.Next;
        ProdottoCursore := TProdotto.Create;
        Result.Next := ProdottoCursore;
       end;
finally
dati.TTempTrattProdotti.Filtered:=False;
dati.TTempTrattProdotti.Locate('Prodotto',mempos,[]);
dati.TTempTrattProdotti.EnableControls;
end;
end;
Titolo: Re:Come assicurarsi del corretto rilascio delle risorse
Inserito da: luigi67 - Marzo 07, 2014, 12:21:37 pm
Ho fatto questa piccola modifica e funziona

Codice: [Seleziona]
while not dati.TTempTrattProdotti.eof do
       begin
       ProdottoCursore.IDProdotto:=dati.TTempTrattProdotti.FieldByName('Prodotto').AsString;
        ProdottoCursore.Quantita:=dati.TTempTrattProdotti.FieldByName('FDQuantita').AsFloat;
        dati.TTempTrattProdotti.Next;
     
    ProdottoCursore.Next := TProdotto.Create;
        ProdottoCursore:=ProdottoCursore.Next;

       end;
finally   
Titolo: Re:Come assicurarsi del corretto rilascio delle risorse
Inserito da: Stilgar - Marzo 07, 2014, 12:57:19 pm
Bene
Titolo: Re:Come assicurarsi del corretto rilascio delle risorse
Inserito da: luigi67 - Marzo 11, 2014, 11:04:36 am
Allora dopo prove e riprove l'errore persiste. La cosa pazzesca è che gli oggetti creati sono creati o dentro procedure con variabili locali o sono essi stessi result di funzioni. Eppure continua a darmi errore. Ho provato pure con backtrace ed è un'ulteriore conferma, la memoria non rilasciata cresce al maggior utilizzo di tali oggetti. es. se ho più di un record. Se ho un solo record backtrace mi dice

7 unfreed memory blocks : 126 

mentre se è di due record

12 unfreed memory blocks : 248

Vorrei allegare il progetto, ma per usarlo dovreste crearvi pure un database firebird, potrei allegare lo script sql per la creazione di un database vuoto

Il dubbio che mi viene è che sia un problema del compilatore o di Lazarus, dato che per usare alcuni package che mi servono li ho presi dalle versioni non stabili

e sono le seguenti

Lazarus 1.3 r43375
FPC 2.7.1 i386-linux-gtk 2

Vi ringrazio ancora per l'aiuto
Luigi
Titolo: Re:Come assicurarsi del corretto rilascio delle risorse
Inserito da: Stilgar - Luglio 05, 2014, 12:20:09 am
Prova a passare le istanze "locali" come parametri "var".
In questo modo non li hai creati nell'heap della funzione chimata, ma in quella chiamante.
Così il lifetime degli oggetti lo dovresti riuscire a controllare meglio .. spero.

Se poi il problema è nei pacchetti beta ... o li ricontrolli o cerchi una versione stabile per essere sicuro :D

Stilgar