PDA

Ver la Versión Completa : Liberar componentes de la memoria


ingel
29-06-2005, 14:43:25
Hola ..debo revisar el codigo de un sistema que funciona pero debes en cuando tira errores de acceso a memoria , el clasico FFFFFFFF etc...
asumo que no debe estar liberando correctamente los componentes ,tanto forms , querys , etc..
La pregunta seria ..donde (en que evento) y como liberar eficientemente los componentes de la memoria , ...cuando se sale del from que los usa (onclose? destroy ?), es necesario hacer el close y el free ? sobre todo de las Querys o lo hace solo el delphi ?
Lo mismo con los forms , ya que la creacion se va concatenando..(un form va creando uno o varios y asi sucesivamente) y creo que ahi se descontrola,al ejecutarse varias veces lo mismo ... por ej. en cada boton que llama a otro form hace el Application.CreateForm y el free
si vuelve a presionar el boton hace lo mismo.... estimo que no es lo mejor..no?
saludos
Ingel

marcoszorrilla
29-06-2005, 14:58:48
En un principio en el OnClose del Formulario pones
Action:=CaFree;

y no te tiene porque dar problemas.

Un Saludo.

ingel
29-06-2005, 15:09:34
debo hacer el free al salir del form o close o ninguno?

GRACIAS

Neftali [Germán.Estévez]
29-06-2005, 15:31:12
..donde (en que evento) y como liberar eficientemente los componentes de la memoria , ...cuando se sale del from que los usa (onclose? destroy ?), es necesario hacer el close y el free ? sobre todo de las Querys o lo hace solo el delphi ?
Los componentes que hay colocados en los formularios no debes liberarlos tú, los liberará el propio formulario cuando lo destruyas. Los que crees tú por código son los que debes destruir tú.
El lugar donde liberarlos depende; La idea es que la visibilidad sea la mínima posible; A parte de eso, el evento OnDestroy es un buen lugar...

jachguate
29-06-2005, 16:06:47
Los que crees tú por código son los que debes destruir tú.

Esto no es siempre así. Hay ocasiones en que podes delegar al mecanismo de "propiedad" de los objetos de delphi la liberación de la memoria.

Me explico. Todos aquellos objetos que pueden pertenecer a otro objeto (formalmente los derivados de TComponent) son automáticamente liberados por su propietario (owner) cuando este se destruye.

Así, si creamos un botón de la siguiente forma:


Var
b : TButton;

begin
b := TButton.Create(Form1);
end;


Estableciendo Form1 como su propietario, la memoria del botón será liberada al destruirse form1.

Si a su vez Form1 pertenece al objeto Application, tanto este como el botón serán liberados automáticamente al terminar la aplicación... etc.

Hasta luego.

;)

ingel
29-06-2005, 16:18:59
en el proyecto hay una unit con todas las funciones comunes (uUtiles) varias de las cuales crean TQuerys en el var

var QTmp : TQuery;

y luego

QTmp:= TQuery.Create(Application);

para luego conectarse a la DB y consultar o hacer un update por ejemplo..

Estas querys se liberan al liberar el form uUtiles ? (lo que se hace al cerrar la aplicacion) con lo cual se estarian creando cientos de querys sin liberar... o se libera cuando devuelve la llamada que le hizo algun form ?

GRACIAS ..

roman
29-06-2005, 16:24:09
asumo que no debe estar liberando correctamente los componentes


Yo no creo que ésta sea una suposición correcta. Un objeto esclavo (no liberado) te dará problemas de goteo (memory leack) pero no de ultraje (access violation). Éstos se dan más bien cuando se intenta acceder a objetos ya libres, de manera que yo buscaría el origen del problema en el uso de objetos que posiblemente ya no existan.

// Saludos

pd: traducción de términos con dedicatoria a jachguate

jachguate
29-06-2005, 17:12:10
Estas querys se liberan al liberar el form uUtiles ? (lo que se hace al cerrar la aplicacion) con lo cual se estarian creando cientos de querys sin liberar... o se libera cuando devuelve la llamada que le hizo algun form ?
Pero... esta decisión no la toma nadie por vos... que sos quien lo decide.

Si no los estas liberando, al ser propiedad de Application, se liberarán hasta le final de la ejecución, cosa que no me parece adecuada para la mayoría de los casos. En todo caso, quien sabe lo que pretende con esto y las necesidades específicas del programa sos vos, o al menos debieras saberlo.. :o

En general es conveniente liberar la memoria tan pronto como el objeto deje de usarse. Así, si una función devuelve un TQuery, lo ideal sería algo como:


Function UnQuery(Parametros : TipoParametros) : TQuery;
Begin
result := TQuery.Create(nil);
result.HagoLoQueTengoQueHacer;
end;

Procedure TForm1.Button1Click(Sender : TObject);

Begin
with UnQuery(Parametros) do
try
ProcesarElQuery;
finally
Free; // Libero el query
end;
end;

//o bien:

Procedure TForm1.Button2Click(Sender : TObject);

Var
Query : TQuery;

Begin
Query := UnQuery(Parametros);
try
ProcesarElQuery(Query);
finally
Query.Free;
end;
end;

{ si el uso escapa a un procedimiento o función, se puede guardar
una referencia en el formulario y destruirlo cuando sea conveniente,
por ejemplo en el evento OnDestroy del formulario }
Type
TForm1 = class(TForm)
{ Lo generado por delphi }
private
FQueryUno : TQuery;
// otros mienbros, métodos y/o propiedades
end;

Procedure TForm1.Button3Click(Sender : TObject);

Begin
if not assigned(FQueryUno) Then
FQueryUno := UnQuery(Parametros);
FQueryUno.First;
etcetera;
end;

Procedure TForm1.Form1Destroy(Sender : TObject);

Begin
if assigned(FQueryUno) Then
FQueryUno.Free;
end;

{ por último, se puede pasar una referencia al objeto que se quiere
dejar como propietario del query dentro de los parámetros de
la función que lo crea, para delegar su destrucción al mecanismo
de liberación automática de memoria de este objeto }

Procedure TForm1.ButtonYaPerdiLaCuentaClick(Sender : TObject);

Var
Query : TQuery;

Begin
Query := UnQuery(Self, OtrosParametros);
// no hace falta liberar el query, porque self será el owner.
end;



pd: traducción de términos con dedicatoria a jachguate
:o :o :o
¿hay algo malo en mi forma de traducir los términos? :confused:

roman
29-06-2005, 17:47:24
¿hay algo malo en mi forma de traducir los términos?


Me refería a esto (http://www.clubdelphi.com/foros/showpost.php?p=94678&postcount=19)

-------------------

Normalmente no es buena idea hacer funciones que regresen objetos creados por ellas mismas precisamente porque es difícil dar seguimiento a cuando deben destruirse.

Sin embargo, si esto es absolutamente necesario yo optaría por el uso de interfaces para que la liberación de memoria sea automática.

La idea sería algo así:



type
IQueryHolder = interface
function GetQuery: TQuery;
property Query: TQuery read GetQuery;
end;

TQueryHolder = class(TInterfacedObject, IQueryCreator)
var
FQuery: TQuery;

public
constructor Create(AQuery: TQuery);
destructor Destroy; override;

function GetQuery: TQuery;
end;

implementation

constructor TQueryHolder.Create(AQuery: TQuery);
begin
FQuery := AQuery;
end;

destructor TQueryHolder.Destroy;
begin
FQuery.Free;
inherited;
end;

function TQueryHolder.GetQuery: TQuery;
begin
Result := FQuery;
end;
end.


La interfaz IQueryHolder simplemente mantiene una referencia a un objeto Query. La clase TQueryHolder implementa esta interfaz. Dado que desciende de TInterfacedObject, su destructor será llamado en cuanto se pierda la última referencia a la interfaz y este destructor se encarga de liberar al objeto Query.

Para usar esta interfaz, la función UnQuery descrita por jachguate se debe modificar así:



function UnQuery(parámetros): IQueryHolder;
var
Query: TQuery;

begin
Query := TQuery.Create(nil);
{ pasar parámetros }
Result := TQueryHolder.Create(Query);
end;


Y para usar el resultado se haría así:



var
QueryHolder: IQueryHolder;

begin
QueryHolder := UnQuery(parámetros);

QueryHolder.Query.Open;
end;


Como dije antes, Sin importar dónde esté declarada la variable QueryHolder, el objeto Query que contiene se liberará en cuanto se pierda la última referencia.

// Saludos

Neftali [Germán.Estévez]
29-06-2005, 17:47:48
Esto no es siempre así. Hay ocasiones en que podes delegar al mecanismo de "propiedad" de los objetos de delphi la liberación de la memoria.
Así, si creamos un botón de la siguiente forma:


Var
b : TButton;

begin
b := TButton.Create(Form1);
end;


Cierto, un pequeño despieste...:(

jachguate
29-06-2005, 18:12:34
Me refería a esto (http://www.clubdelphi.com/foros/showpost.php?p=94678&postcount=19)


:D:D Lo siento... no habia captado el asunto... pero efectivamente me convenzo mas cada vez de que algo tenes que ver en la traducción de libros.. :D

Sin embargo, si esto es absolutamente necesario yo optaría por el uso de interfaces para que la liberación de memoria sea automática.
Es simplemente una idea genial... gracias por compartirla!

TInterfacedObject serves as a convenient base class for classes that implement interfaces because it implements the methods of IInterface. TInterfaceObject descendants use the IInterface reference-counting methods to handle lifetime management. That is, when the reference count on a TInterfaceObject descendant drops to zero, the object is automatically freed.

Hasta luego.

;)

roman
29-06-2005, 18:30:09
Los puristas dicen que las interfaces no se hicieron para implementar un recolector de basura. Pero como a mi esto me tiene sin cuidado se puede incluso ir más lejos. En lugar del QueryHolder se hace una interfaz que "publique" los métodos y propiedades principales del objeto Query:



type
IQuery = interface
function ParamByName(const Value: String): TParam;
function FieldByName(const FieldName: string): TField;

procedure Open;
procedure ExecSQL;
end;

TQueryObject = class(TInterfacedObject, IQuery)
private
FQuery: TQuery;

public
constructor Create;
destructor Destroy; override;

{ IQuery }
(*
La clase implementa estas funciones simplemente delegándolas
a su objeto privado FQuery. Por ejemplo:

ParamByName --> Result := FQuery.ParamByName(param);
*)
function ParamByName(const Value: String): TParam;
function FieldByName(const FieldName: string): TField;

procedure Open;
procedure ExecSQL;
end;

implementation

constructor TQueryObject.Create;
begin
FQuery := TQuery.Create;
end;

destructor TQueryObject.Destroy;
begin
FQuery.Free;
inherited;
end;

function UnQuery(parámetros): IQuery;
begin
Result := TQueryObject.Create;
Result.ParamByName(param).AsXXX := param;
end;

end.


Y se usaría simplemente así:


var
Query: IQuery;

begin
Query := UnQuery(parámetros);
Query.Open;
end;


De esta manera, el código que use lo devuelto por funciones como UnQuery se ve igual que si se hubiera usado un TQuery directamente.

// Saludos