Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   OOP (https://www.clubdelphi.com/foros/forumdisplay.php?f=5)
-   -   FreeAndNil VS Free (https://www.clubdelphi.com/foros/showthread.php?t=73190)

GerTorresM 06-04-2011 17:27:04

FreeAndNil VS Free
 
Hola a tod@s:

Bien la duda que me asalta el día de hoy es la siguiente:

cual es la función mas indicada para destruir un objeto para este caso un formulario

Código Delphi [-]
procedure TFMenuPrincipal.ANovedadesExecute(Sender: TObject);
Var FNovedades_temporal : TFNovedades;
begin
  try
    FNovedades_temporal:= TFNovedades.Create(self);
    FNovedades_temporal.ShowModal;
  finally
    FNovedades_temporal.Free;
  end;
end;

o

Código Delphi [-]
procedure TFMenuPrincipal.ANovedadesExecute(Sender: TObject);
Var FNovedades_temporal : TFNovedades;
begin
  try
    FNovedades_temporal:= TFNovedades.Create(self);
    FNovedades_temporal.ShowModal;
  finally
    FreeAndNil(FNovedades_temporal);
  end;
end;
y muy en concreto cual es la diferencia entre las dos funciones


Agradezco de antemano



gertorresm
Colombia

oscarac 06-04-2011 17:32:17

muy buena pregunta....
no me habia puesto a indagar sobre el tema

yo libero los formularios asi

Código Delphi [-]
 
frmBalance := nil;

roman 06-04-2011 17:42:24

Cita:

Empezado por GerTorresM (Mensaje 396044)
cual es la función mas indicada para destruir un objeto para este caso un formulario

[...]

y muy en concreto cual es la diferencia entre las dos funciones

En términos llanos, FreeAndNil lo que hace es llamar al método Free del objeto y luego asignar nil a la variable que hace referencia al objeto. Dicho de otra forma:

Código Delphi [-]
FreeAndNil(Obj);

es equivalente a

Código Delphi [-]
Obj.Free;
Obj := nil;

La única razón para usar FreeAndNil al momento de liberar un objeto es asegurarte que el objeto apunta a nil y no a una dirección de memoria arbitraria. Esto te puede servir si posteriormente utilizas la variable y quieres saber si sigue "vivo":

Código Delphi [-]
if Assigned(Obj) then
  ...

Pero, en el caso que propones, siendo una variable temporal que no se usa posteriormente, no tiene nonguna ventaja poner la variable en nil ni, por tanto, usar FreeAndNil.

// Saludos

roman 06-04-2011 17:43:19

Cita:

Empezado por oscarac (Mensaje 396045)
yo libero los formularios asi

Código Delphi [-]
 
frmBalance := nil;

Así no estás liberado nada. Simplemente apuntas la variable frmBalance a nil pero el objeo sigue existiendo en memoria. Tienes que usar Free.

// Saludos

gluglu 06-04-2011 17:43:54

Para preguntar en cualquier parte de tu programa si uno de tus formularios concretos está o no creado, al menos yo, pregunto por
Código Delphi [-]
if Asigned(MiForm) then ....
La única manera de que te devuelva False, es decir, que el Formulario no está creado, es que MiForm sea igual a Nil

Si Haces sólo un Free del formulario, no se pone a 'Nil' y por lo tanto la pregunta anterior siempre te dará True incluso aunque hayas 'liberado' tu formulario con Free.

Por eso FreeAndNil, o simplemente también sirve :
Código Delphi [-]
MiForm.Free
MiForm := nil;

... sobre gustos ...

gluglu 06-04-2011 17:44:46

... se me adelantó el Maestro Roman !! :o

oscarac 06-04-2011 17:45:14

como comentario adicional encontre esto que quiza te ayude

oscarac 06-04-2011 18:05:55

mmm me puse a cambiar los...

frmBalance := nil

por

FreeandNil(frmBalance)

y me sale el siguiente mensaje de error cuando cierro un formulario

Invalid Pointer operation


what da faq????

oscarac 06-04-2011 18:21:37

la forma como creo los formularios es asi (dentro de un formulario Main)

Código Delphi [-]
 
frmBalance := TfrmBalance.create(nil);
frmbalance.show;


cuando coloco

freeandnil(frmbalance) en el destroy o en el close....me sale el error mencionado

pero si coloco esto en el destroy no me aparece mensaje

Código Delphi [-]
 
frmbalance := nil;
frmBalance.free;

me libera ? si? no?

antes de llamar al formulario balance coloque esto

Código Delphi [-]
 
if frmBalance = nil
  frmBalance := TfrmBalance.create(nil);
y siempre es nil

me olvidaba
coloco

action := caFree; en el close

esta bien asi?

roman 06-04-2011 18:23:28

Habría que ver el contexto. Por ejemplo, si llamas FreeAndNil desde el mismo formulario frmBalance, podría suceder el error que pones. En ese caso tendrías que usar Release en lugar de Free.

// Saludos

oscarac 06-04-2011 18:26:53

me atrevo a preguntar

cual es la diferencia entre

frmBalance := TfrmBalance.create(nil) ;

y

frmBalance := TfrmBalance.create(self);

oscarac 06-04-2011 18:28:54

Cita:

Empezado por roman (Mensaje 396054)
Habría que ver el contexto. Por ejemplo, si llamas FreeAndNil desde el mismo formulario frmBalance, podría suceder el error que pones. En ese caso tendrías que usar Release en lugar de Free.

// Saludos

asi es.. lo estaba llamando dentro del mismo formulario frmBalance

entonces lo correcto seria

frmBalance.nil;
frmBalance.free;

???

Casimiro Noteví 06-04-2011 18:29:42

No, no puedes hacerlo así, primero hay que liberar la memoria y luego asignarle nil a la misma.
Por eso el orden correcto es

x.free;
x:=nil;

o más cómodo:

freeandnil(x);

Si lo haces al revés, asignas nil, entonces cuando llamas a free no hace nada porque está a nil.


Edito: he contestado lo de antes sin haber visto tu último mensaje :)

oscarac 06-04-2011 18:33:32

Concluyendo....

primero free y despues nil

y nunca nu freeandnil dentro del mismo form

Casimiro Noteví 06-04-2011 18:45:05

Cita:

Empezado por oscarac (Mensaje 396059)
Concluyendo....
primero free y despues nil
y nunca nu freeandnil dentro del mismo form

Eso sería como levantarte tú mismo del suelo tirando de los cordones de tus botas :D

El boot :)

oscarac 06-04-2011 18:48:09

mmmm
deberias ver a los acrobatas que hay por aca...
desafian las reglas de la fisica :D:D:D

maeyanes 06-04-2011 19:05:04

Hola...

oscarac, no es recomendable usar la variable que declara Delphi cuando creas un formulario nuevo:

Código Delphi [-]
type
  TMyForm = class(TForm)
  // ...
  end;

var
  MyForm: TMyForm; // <--- esta

dentro de los métodos o manejadores de eventos del mismo.

Estó es, si haces algo como:

Código Delphi [-]
procedure TMyForm.FormDestroy(Sender: TObject);
begin
  MyForm := nil
end;

Te podría traer problemas posteriores.

Si quieres garantizar que un formulario se destruye al cerrarlo una de las formas de lograrlo es el evento OnClose:

Código Delphi [-]
procedure TMyForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree
end;


Saludos...

oscarac 06-04-2011 19:15:44

sigo con el tema...

coloco

frmbalance.free
frmbalance.nil

y me sale stack overflow :confused::confused::confused:

rgstuamigo 06-04-2011 19:25:34

Cita:

Empezado por oscarac (Mensaje 396066)
sigo con el tema...

coloco

frmbalance.free
frmbalance.nil

y me sale stack overflow :confused::confused::confused:

Pero.. en qué parte de tu código fuente colocas esas instrucciones? ¿Algun evento específico? ¿dónde?:confused:
Saludos...:)

maeyanes 06-04-2011 19:28:09

Hola...

De seguro está poniendo ese código en el evento OnDestroy del formulario en cuestión...


Saludos...

oscarac 06-04-2011 19:32:23

estuve leyendo mastering delphi

y en un ejemplo dice que para destruir un formulario

basta con colocar en el evento Close

action := caFree
frmBalance := nil

aqui.. en la pagina 359

entonces asi lo estaba haciendo desde un principio

roman 06-04-2011 19:58:42

Liberar un objeto dentro de su propio destructor no tiene sentido. Si se llama al destructor es porque el objeto ya está siendo liberado.

// Saludos

Chris 06-04-2011 20:10:19

Cita:

Empezado por maeyanes (Mensaje 396064)
oscarac, no es recomendable usar la variable que declara Delphi cuando creas un formulario nuevo:
Código Delphi [-]
type
  TMyForm = class(TForm)
  // ...
  end;

var
  MyForm: TMyForm; // <--- esta

dentro de los métodos o manejadores de eventos del mismo.

Estó es, si haces algo como:

Código Delphi [-]
procedure TMyForm.FormDestroy(Sender: TObject);
begin
  MyForm := nil
end;

Te podría traer problemas posteriores.

Como comentario, no se trata que no sea recomendable utilizar la variable declarada por Delphi. De hecho, sí lo es. Lo que pasa es que nunca se debería utilizar esa variable para controlar aspectos del formulario. Siempre hay que utilizar la referencia Self para este menester. Usa la variable auto declarada por Delphi solo cuándo vallas a crear el formulario.

ecfisa 06-04-2011 20:16:31

Hola oscarac.

Me gusta más:
Código Delphi [-]
procedure TForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action:= caFree;
end;

procedure TForm.FormDestroy(Sender: TObject);
begin
  Form:= nil;
end;

Agrego un Koan de Ian Marteens que leí hace un buen tiempo y me pareció muy interesante: ¿Hay vida después de la muerte?.

Un saludo.

javier7ar 06-04-2011 20:19:28

Cita:

Empezado por oscarac (Mensaje 396071)
estuve leyendo mastering delphi

y en un ejemplo dice que para destruir un formulario

basta con colocar en el evento Close

action := caFree
frmBalance := nil

De la ayuda de Delphi, sobre el metodo Free
Cita:

Warning: Never explicitly free a component within one of its own event handlers or the event handler of a component it owns or contains. For example, do not free a button or the form that owns the button in its OnClick event handler.
O sea, que si pones frmBalance.Free (que no es lo mismo que action:=caFree) o si llamas a FreeAndNil(frmBalance) dentro del OnClose de frmBalance o dentro del OnDestroy (que no tendria sentido) te va a dar error.

Lo que haces vos esta bien, porque vos abris las ventanas con Show, entonces las liberas cuando se cierran en el OnClose (con action:=caFree)
Lo que NO podes hacer es cambiar la instruccion action:=caFree; por frmBalance.Free; porque ahi si te va a dar error.

Cuando se usa ShowModal para mostrar las ventanas, la siguiente linea al ShowModal no se ejecuta hasta que se cierra la ventana, entonces se hace la liberacion en la linea siguiente:

Código Delphi [-]
frmBalance:=TfrmBalance.Create(Self);
frmBalance.ShowModal;
frmBalance.Free;
frmBalance:=nil; 
// ó FreeAndNil(frmBalance) para hacerlo en una sola linea


por ultimo, de la ayuda de Delphi sobre el metodo Free...
Cita:

To free a form, call its Release method, which destroys the form and releases the memory allocated for it after all its event handlers and those of the components it contains are through executing.
... aunque yo siempre use Free; jeje

Saludos

rgstuamigo 06-04-2011 20:23:13

Yo tambien uso el modelo de ecfisa, pero cabe destacar que tambien se puede hacerlo todo en el mismo evento OnClose de la forma siguiente:
Código Delphi [-]
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action:=caFree;{todos sabemos que aquí se manda a destruir el formulario
 con el procedimiento Release que  su vez hace uso de la API PostMessage 
que lógicamente liberará al formulario cuando se pueda y no de inmediato
 es por eso que liberar un formulario con el método Release es más flexible, para un mejor comprension ruego buscar "release" en la ayuda de Delphi}
Form2:=nil;
end;

EDITO: Viendo éste asunto me pregunto :rolleyes: sino sería util un método ReleaseAndNil?? ;)
Saludos...:)

roman 06-04-2011 20:46:48

Cita:

Empezado por rgstuamigo (Mensaje 396078)
EDITO: Viendo éste asunto me pregunto :rolleyes: sino sería util un método ReleaseAndNil??

Pues no. Porque en realidad, esto:

Código Delphi [-]
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action:=caFree;
Form2:=nil;
end;

es una mala práctica. Una clase no debería hacer referencia a una instancia en particular. Yo sé que esto es muy común pero no debería hacerse de esta manera.

De entrada, no debería usarse la variable Form2 que el IDE crea en automático por la simple razón de que no es bueno usar variables globales. Si el formulario principal requiere una instancia de Form2 y necesita tenerla reservada, entonces se declara una variable de tipo TForm2 (o, mejor, de tipo TForm) como campo privado.

// Saludos

maeyanes 06-04-2011 20:49:11

Hola

Cita:

Empezado por roman (Mensaje 396080)
De entrada, no debería usarse la variable Form2 que el IDE crea en automático por la simple razón de que no es bueno usar variables globales. Si el formulario principal requiere una instancia de Form2 y necesita tenerla reservada, entonces se declara una variable de tipo TForm2 (o, mejor, de tipo TForm) como campo privado.

// Saludos

Esto es lo que intenté decir en mi mensaje (ver página anterior), pero roman lo explicó mejor :D


Saludos...

oscarac 06-04-2011 20:55:59

quiza no leyeron un mensaje anterior donde preguntaba cual es la diferencia entre

Código Delphi [-]
 
 
Tform1.Create(nil)
 
Tform1.Create(Self)

roman 06-04-2011 21:00:48

Cita:

Empezado por oscarac (Mensaje 396082)
quiza no leyeron un mensaje anterior donde preguntaba cual es la diferencia entre

Código Delphi [-]
 
 
Tform1.Create(nil)
 
Tform1.Create(Self)

Cierto. Ahora te contesto:

Pregunta nueva, hilo nuevo.

// Saludos

rgstuamigo 06-04-2011 21:07:32

Cita:

Empezado por roman (Mensaje 396080)
Pues no. Porque en realidad, esto:

Código Delphi [-]
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action:=caFree;
Form2:=nil;
end;

es una mala práctica. Una clase no debería hacer referencia a una instancia en particular. Yo sé que esto es muy común pero no debería hacerse de esta manera.

De entrada, no debería usarse la variable Form2 que el IDE crea en automático por la simple razón de que no es bueno usar variables globales. Si el formulario principal requiere una instancia de Form2 y necesita tenerla reservada, entonces se declara una variable de tipo TForm2 (o, mejor, de tipo TForm) como campo privado.

// Saludos

Estoy de acuerdo con lo que dices supongo que te estás refiriendo a que el código quede más limpio, es decir, no hacer que en mi clase haga refencias aun objeto en cuestion, pero la cuestion es que: dime ¿Cómo harias para que al momento de destruir mi formulario con Action:=caFree, hagas que dicho formulario apunte a nulo (nil)?:confused:

roman 06-04-2011 21:29:39

Cita:

Empezado por rgstuamigo (Mensaje 396088)
¿Cómo harias para que al momento de destruir mi formulario con Action:=caFree, hagas que dicho formulario apunte a nulo (nil)?:confused:

Por ejemplo...

// Saludos

rgstuamigo 07-04-2011 15:18:08

Cita:

Empezado por roman (Mensaje 396092)
Por ejemplo...

// Saludos

Pues creo no me he explicado bien...:o.. bueno el ejemplo que pones en ese hilo hace uso de los métodos FreeNotification y Notification que son válido si se quiere hacer de cualquier forma,yo diría que es otra forma de hacer lo mismo que hice en el post 26,claro de una forma un poco más elegante :cool:, si te dás cuenta igualmente estás haciendo uso de un variable privada, fuera de la clase :eek: a la que pertenece tu variable "ChildForm" es decir mi pregunta era que dentro de la clase "TChildForm"(siguiendo tu ejemplo) sin salir fuera de ella y sin hacer uso de una variable, llamese ésta global o privada puedas lograr que cualquier objeto instanciado que pertenesca a la clase "TChildForm" al momento de destruirse se ponga nulo (nil).;) Es decir, el formulario donde se está creando (instaciando) objeto de la clase "TChildForm" no tiene por que hacer el trabajo de poner a dichos objetos en nulo, cada objeto al momento de destruirse ya, él por si mismo debería ponerse en nulo. ¿me entiendes?, es por eso que pensé que sería bueno contar con un método ReleaseAndNil, a lo cual te opusiste.:(
Saludos...:)

roman 07-04-2011 17:15:52

Cita:

Empezado por rgstuamigo (Mensaje 396147)
cada objeto al momento de destruirse ya, él por si mismo debería ponerse en nulo. ¿me entiendes?

Te entiendo, pero no estoy de acuerdo por dos razones:

1. Quien se pone nulo no es el objeto sino una referencia al objeto. Diferencia sutil pero importante. Puedes tener múltiples referencias a un mismo objeto, así que, ¿cuál de ellas es la que se pondría en nulo al momento de destruir el objeto? Eso es algo que puede decidir quien creó al objeto, mas no el objeto en sí.

2. Como no sea una suerte de patrón singleton, por lo general puedes tener varias instancias de una misma clase. Aún en el caso de formularios que en un determinado contexto quieres abrir una sola vez. Entonces, ¿a cuál de estas instancias se debe referir el destructor de la clase?

Por otra parte,

Cita:

Empezado por rgstuamigo
yo diría que es otra forma de hacer lo mismo que hice en el post 26,claro de una forma un poco más elegante , si te dás cuenta igualmente estás haciendo uso de un variable privada, fuera de la clase

no es que sea más elegante, sino que es fundamentalmente distinta. Mi variable privada se anula fuera de la clase y lo hace quien creo la instancia.


Yo diría, en una especie de resumen, que las referencias a un objeto son entidades ajenas a la clase del objeto (y al objeto mismo).

// Saludos

rgstuamigo 07-04-2011 18:35:15

Cita:

Empezado por roman (Mensaje 396164)
Te entiendo, pero no estoy de acuerdo por dos razones:

1. Quien se pone nulo no es el objeto sino una referencia al objeto.
...
2. Como no sea una suerte de patrón singleton, por lo general puedes tener varias instancias de una misma clase.

Bueno eso ya sabemos amigo roman y eso no está en discucion...;) no mezclemos las cosas;)

Cita:

Empezado por roman (Mensaje 396164)
Por otra parte,

...
no es que sea más elegante, sino que es fundamentalmente distinta. Mi variable privada se anula fuera de la clase y lo hace quien creo la instancia.


Yo diría, en una especie de resumen, que las referencias a un objeto son entidades ajenas a la clase del objeto (y al objeto mismo).

Haber... tú mismo criticaste el hecho de que se usara la variable global que genera Delphi a crear un formualrio, pero si analizamos tu solucion pues basicamente hace lo mismo, claro ahora ya no es una variable Global sino privada:D, pero de igual manera se la está usando practicamente para hacer lo mismo, pero mejor me explico con un ejemplo siguiendo tu propia solución:
Si por ejemplo instanciaramos un objeto de la clase "TChildForm" de la siguiente forma:
Código Delphi [-]
if otroForm = nil then// OtroForm es otra instacia diferente de ChildForm
 begin
   otroForm := TChildForm.Create(Self);
   otroForm.FreeNotification(Self);
 end;
otroForm.Show;
Para que tu solucion funcione tambien con "OtroForm" deberiamos modifcar el método Notification de la siguiente forma:
Código Delphi [-]
procedure TParentForm.Notification(AComponent: TComponent; Operation: TOperation);
begin
  inherited;

  if (AComponent = ChildForm) and (Operation := opRemove) then
    ChildForm := nil;
//lineas aumentadas
 if (AComponent = OtroForm) and (Operation := opRemove) then
    OtroForm := nil;
{ohora te podas imaginar que ocurre si seguimos creando mas instancias con diferentes referencias.. :eek: }
end;
Eso quiere decir que por cada nueva referencia tengo que aumentar código al método Notification..y eso no es ideal amigo seamos realista.;)
En otras palabras estamos restringidos a crear un objeto solo atraves de la variable "ChildForm", si quisieramos que la cosa siga funcionando y evitar agregar más código; y..pues es practicamente lo mismo que hacer uso de la varible global que gerera delphi, asi que por ese lado no hay diferencia.;)
Bueno... para no hacerla muy larga la cuestion...pues voy a volver a hacer la pregunta:
¿Es posible implementar una solucion que esté dentro de la misma clase sin utilizar alguna variable en sí? es decir al momento de Destruir(Action:=caFree;) el Objeto hacer que la referencia del objeto sea nula(nil)??:confused: ¿será posible eso?:confused:

roman 07-04-2011 18:43:52

Dices esto:

Cita:

Empezado por rgstuamigo (Mensaje 396180)
Bueno eso ya sabemos amigo roman y eso no
está en discucion...;) no mezclemos las cosas;)

y sin embargo preguntas esto

Cita:

Empezado por rgstuamigo (Mensaje 396180)
¿Es posible implementar una solucion que esté dentro de la misma clase sin utilizar alguna variable en sí? es decir al momento de Destruir(Action:=caFree;) el Objeto hacer que la referencia del objeto sea nula(nil)??:confused: ¿será posible eso?:confused:

¿Cuál objeto? ¿Cuál de todos los objetos que pueden crearse de una clase quieres que se ponga en nulo? Pero, sobre todo, ¿cuál de todas las referencias al objeto quieres que se ponga nula? Pues, aunque lo desestimas, es una diferencia importante que, por tu argumentación, parece que no tienes clara.

// Saludos

rgstuamigo 07-04-2011 19:50:21

Cita:

Empezado por roman (Mensaje 396181)
..
¿Cuál objeto? ¿Cuál de todos los objetos que pueden crearse de una clase quieres que se ponga en nulo? Pero, sobre todo, ¿cuál de todas las referencias al objeto quieres que se ponga nula? Pues, aunque lo desestimas, es una diferencia importante que, por tu argumentación, parece que no tienes clara.

// Saludos

Bueno...:rolleyes: a lo que me refiero roman es a que si es posible que cualquier referencia, al destruirse el objeto al que apunta, se pueda poner en nula, pero lo que se quiere es que la solucion para eso esté dentro de la misma clase no fuera de ella...;) ¿Es posible hacerlo o no?
Por favor responde la pregunta...sin vueltas...

maeyanes 07-04-2011 19:57:46

Hola...

Cita:

Empezado por rgstuamigo (Mensaje 396187)
Bueno...:rolleyes: a lo que me refiero roman es a que si es posible que cualquier referencia, al destruirse el objeto al que apunta, se pueda poner en nula, pero lo que se quiere es que la solucion para eso esté dentro de la misma clase no fuera de ella...;) ¿Es posible hacerlo o no?
Por favor responde la pregunta...sin vueltas...

Pues el ya mencionado FreeAndNil ya realiza lo que pides.


Saludos...

rgstuamigo 07-04-2011 20:00:14

Cita:

Empezado por maeyanes (Mensaje 396188)
Hola...



Pues el ya mencionado FreeAndNil ya realiza lo que pides.


No maeyanes.. eso es fuera de la clase..;) y aparte estamos hablando de destruir con el método Release y no con Free...;)

roman 07-04-2011 20:15:17

Cita:

Empezado por rgstuamigo (Mensaje 396187)
Por favor responde la pregunta...sin vueltas...

No sé.

// Saludos


La franja horaria es GMT +2. Ahora son las 10:47:10.

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