Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Varios (https://www.clubdelphi.com/foros/forumdisplay.php?f=11)
-   -   Liberación de un TstringList (https://www.clubdelphi.com/foros/showthread.php?t=92810)

geolife 08-02-2018 12:00:49

Liberación de un TstringList
 
Hola amig@s,

Me gustaría saber vuestra opinión sobre la forma correcta de asignar un objeto y su liberación; Creo que es una pregunta seguramente muy simple, pero bueno...

En el método 1, al ser una variable local de un procedimiento ¿Se liberarían los recursos si hubiese alguna excepción después de la asignación?, ¿o debemos forzar con el segundo método siempre?

Tenemos la siguiente función que delimita una cadena de texto:

Código Delphi [-]
Function TForm1.DelimitarString(Cadena: String; Delimitador: Char): TStringList;
begin
  Result := TStringList.Create;
  Result.Delimiter := Delimitador;
  Result.StrictDelimiter := True;
  Result.DelimitedText := Cadena;
end;

// Método 1
Procedure TForm1.TratarCadena1;
Var
  St: TStringList;
begin
  St := DelimitarString('Uno;Dos;Tres', ';');
  Showmessage(St[0] + #13 + St[1] + #13 + St[2]);
end;

// Método 2
Procedure TForm1.TratarCadena2;
Var
  St: TStringList;
begin
  St := TStringList.Create;
  Try
    St := DelimitarString('Uno;Dos;Tres', ';');
    Showmessage(St[0] + #13 + St[1] + #13 + St[2]);
  Finally
    St.Free;
  end;
end;

Neftali [Germán.Estévez] 08-02-2018 16:51:01

Cita:

Empezado por geolife (Mensaje 524441)
En el método 1, al ser una variable local de un procedimiento ¿Se liberarían los recursos si hubiese alguna excepción después de la asignación?, ¿o debemos forzar con el segundo método siempre?

Pues en este caso ninguno de los 2 métodos te liberra correctamente. Porque fíjate que en el METODO 1 creas 1 (en el procedimiento DelimitarString) y no liberas ninguno y en el METODO 2 creas 2 y sólo liberas 1.

Además Delphi te provee un método para revisar esto:

En el DPR añade la siguiente línea:

Código Delphi [-]
    ReportMemoryLeaksOnShutdown := DebugHook <> 0;  
    Application.Initialize;
    ...

Al ejecutar tu programa con estos 2 método al acabar saldrá una ventana como estas:





En ambos casos ves que estás perdiendo memoria.


Una solución correcta (para no perder memoria) aunque en mi opinión tampoco correcta del todo, sería esta:

Código Delphi [-]
// Metodo 2

Var
  St: TStringList;
begin
  St := DelimitarString('Uno;Dos;Tres', ';');         <-- Aquí dentro la creas
  Showmessage(St[0] + #13 + St[1] + #13 + St[2]);
  FreeAndNil(St);                                      <--  Aquí lo  liberas

En este último caso no aparece la venana de pérdida de memoria, aunque comento que NO es correcta en mi opninión, porque yo tengo una máxima: "Quien crea un opbjeto es el encargado de liberarlo"

Así que siguiendo esas indicaciones un código correcto para mi, sería este:

Código Delphi [-]
Function TForm2.DelimitarString(Cadena: String; Delimitador: Char; var TS:TStringList): TStringList;
begin
  TS.Delimiter := Delimitador;
  TS.StrictDelimiter := True;
  TS.DelimitedText := Cadena;
end;

procedure TForm2.Button2Click(Sender: TObject);
Var
  St: TStringList;
begin
  St := TStringList.Create;
  Try
    DelimitarString('Uno;Dos;Tres', ';', St);
    Showmessage(St[0] + #13 + St[1] + #13 + St[2]);
  Finally
    St.Free;
  end;
end;

Hay otras si sigues el mismo "precepto"...

duilioisola 08-02-2018 16:51:35

El método 2 no libera al primer St Creado.
Al método 1 le agregué el TRY..FINALLY por si falla algo entre que se crea y se libera.
Código Delphi [-]
Function TForm1.DelimitarString(Cadena: String; Delimitador: Char): TStringList;
begin
  Result := TStringList.Create;
  Result.Delimiter := Delimitador;
  Result.StrictDelimiter := True;
  Result.DelimitedText := Cadena;
end;

// Método 1
Procedure TForm1.TratarCadena1;
Var
  St: TStringList;
begin
  St := DelimitarString('Uno;Dos;Tres', ';');
  // Si llega aquí, St se ha creado correctamente. 
  // Agrego Try..Finally para asegurarme de que se libere, pase lo que pase.
  try
     Showmessage(St[0] + #13 + St[1] + #13 + St[2]);
  finally
     St.Free;
  end;
end;

// Método 2
Procedure TForm1.TratarCadena2;
Var
  St: TStringList;
begin
  St := TStringList.Create;
  Try
    // Esto machaca el St creado con otro que creará dentro de la función DelimitarString 
    St := DelimitarString('Uno;Dos;Tres', ';');
    Showmessage(St[0] + #13 + St[1] + #13 + St[2]);
  Finally
    St.Free;
  end;
end;

De todos modos, a mi me gusta mas crear los objetos y pasárselo a los procedimientos para que los rellenen.
Por ejemplo le puedes pasar la lista de items de un componente TCombo... para que te lo rellene.
Código Delphi [-]
procedure RellenaAlmacenes(Lista: TStrings);
var
  Idalmacen : integer;
begin
  Lista.Clear;
  Lista.AddObject(_('Todos los Almacenes'), Pointer(0));

  // Abro y fitro tabla de almacenes activos
  ...
  while not TablaAlmacenes.EOF do
  begin
     Idalmacen := FieldByName('ID').AsInteger;
     Almacen := FieldByName('ALMACEN').AsString + ' ' + FieldByName('DESCRIPCION').AsString;
     Lista.AddObject(Almacen, Pointer(IdAlmacen));
     Next;
  end;
end;

begin
  ...
  RellenaAlmacenes(CBAlmacen.Items);
  CBAlmacen.ItemIndex := 0;
  ...
end;

Según tu ejemplo sería algo asi:
Código Delphi [-]
procedure TForm1.DelimitarString(St: TStringList; Cadena: String; Delimitador: Char);
begin
  St.Delimiter := Delimitador;
  St.StrictDelimiter := True;
  St.DelimitedText := Cadena;
end;

Procedure TForm1.TratarCadena1;
Var
  St: TStringList;
begin
  St := TStringList.Create;
  // Si llega aquí, St se ha creado correctamente. 
  // Agrego Try..Finally para asegurarme de que se libere, pase lo que pase.
  try
     DelimitarString(St, 'Uno;Dos;Tres', ';');
     Showmessage(St[0] + #13 + St[1] + #13 + St[2]);
  finally
     St.Free;
  end;
end;

geolife 09-02-2018 12:47:00

Buenos días Neftali [Germán.Estévez]; [duilioisola]

Muchas gracias por tomaros la molestia de contestar. Todos los días se aprende algo aquí, no conocía este truco para comprobar las pérdidas de memoria, a mi pesar, sigo “abusando” del showmessage como forma de depuración principal...algo arcaico, ya lo sé.

Queda claro que la forma más elegante y segura de gestionar el objeto es creándolo y liberándolo en el mismo procedimiento, y pasando éste por referencia al procedimiento que solo estará encargado establecer la delimitación del texto; también la necesidad de asegurar el constructor con un try except en caso de usar la función como originalmente.

Muchas gracias!
||-||



En el DPR añade la siguiente línea:

Código Delphi [-]
ReportMemoryLeaksOnShutdown := DebugHook <> 0;       
Application.Initialize;     
...


La franja horaria es GMT +2. Ahora son las 04:14:31.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi