Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Varios (https://www.clubdelphi.com/foros/forumdisplay.php?f=11)
-   -   Uso de nil (https://www.clubdelphi.com/foros/showthread.php?t=26065)

CelestronFan 12-10-2005 06:31:00

Uso de nil
 
Saludos a todos,

Por favor, alguien podría explicarme brevemente que significa el nil?

Normalmente yo acostumbro a poner por ejemplo:

Código Delphi [-]
     SerialPort.Caption := 'Connected';

pero veo a menudo que los ejemplos y la ayuda coloca:

Código Delphi [-]
   if SerialPort <> nil then
       SerialPort.Caption := 'Connected';

En realidad no entiendo para que es esa comparación con nil.

Gracias.

dec 12-10-2005 11:09:40

Hola,

La palabra reservada "nil" indica en Delphi una dirección indeterminada en la memoria. Vale asignar "nil" a un puntero, de manera que este no apunte a ninguna dirección de memoria en concreto. Una variable cuya referencia es un tipo de clase es en realidad un puntero que referencia a determinado objeto, de ahí que pueda asignarse también a este tipo de variables "nil".

En el caso que te ocupa se está preguntando (más o menos) lo siguiente: "¿SerialPort apunta a alguna dirección de memoria en concreto? Sí es así, ejecútese la instrucción siguiente; si no es así deje de ejecutarse la misma". Lo mismo no me expliqué correctamente pero eso tengo yo entendido que significa la palabra reservada "nil".

Por poner un ejemplo, cuando se destruye un determinado objeto su variable no apuntará ya a la dirección de memoria en que se hacía referencia (si alguna vez fue instanciada la clase de objeto en cuestión) a dicho objeto, y, por lo tanto, la variable de marras será "nil".

Lepe 12-10-2005 12:28:10

Cita:

Empezado por dec
Por poner un ejemplo, cuando se destruye un determinado objeto su variable no apuntará ya a la dirección de memoria en que se hacía referencia (si alguna vez fue instanciada la clase de objeto en cuestión) a dicho objeto, y, por lo tanto, la variable de marras será "nil".

Si usamos Objeto.Free la variable Objeto se queda con la dirección de memoria donde estaba creada la variable, por ende, es mejor usar siempre FreeAndnil(Objeto) para que se quede apuntando a nil.

Tambien el código:
Código Delphi [-]
 if SerialPort <> nil then
       SerialPort.Caption := 'Connected';
se puede traducir como: ¿el Objeto SerialPort ha sido creado previamente?:
- Si, por tanto se puede acceder a su propiedad Caption, así que la modificamos.
- No, entonces, si intentamos acceder a una propiedad (Caption en este caso) que en realidad no existe en memoria, estamos traspasando un puntero nulo y obtendríamos un Access Violation.

Como se puede apreciar, es una pregunta de seguridad. Al tiempo de destruir el objeto SerialPort, seguro que pone FreeAndnil(SerialPort).

Resumiendo:
- Si declaramos una variable que desciende de Tobject, como valor primario debe tener nil (delphi ya lo hace por nosotros), aunque yo prefiero poner Objeto := nil (para clarificar el código)
- Cuando vamos a crear el objeto, miramos si tiene nil:
  1. Si tiene nil, creamos el objeto.
  2. Si no tiene nil, significa que ya ha sido creado, por tanto accedemos a él.

Las funciones Assigned y FreeAndnil nos ayudan bastante en nuestro código:

Assigned(objeto) es identico a poner if Objeto <> nil then
Código Delphi [-]

private 
  nodo:TTreeNode;
end;

implementation

// inicializamos el valor de nodo
procedure TForm1.Form1Create;
begin
  nodo := nil;
end;

// accedemos al nodo o lo creamos si es necesario
procedure TForm1.button1Click;
begin
  if assigned(nodo) then 
    nodo.text:= 'si ';
  else
    nodo := TTreenode.Create(self);
end;

// al salir liberamos el nodo
// si tienen nil no hace nada
// si ha sido creado, lo libera
procedure Tform1.FormClose;
begin
  freeandnil(nodo);
end;

saludos

lucasarts_18 12-10-2005 14:38:31

Hola.

Aquí dejo otro ejemplo:

Código Delphi [-]
 procedure TFrmInterfaz.PageControlChange(Sender: TObject);
 begin
   if PageControl.ActivePage <> TabSheet3 then
   begin
     if Assigned(InterfEntrada) then
        //como dice Lepe es lo mismo que if InterfEntrada <> nil
     begin
       DS.DataSet := nil;
       FreeAndNil(InterfEntrada);
     end;
   end
   else
     if PageControl.ActivePage = TabSheet3 then
       try
         InterfEntrada :=  TOracleDataset.Create (nil);
         InterfEntrada.Session := DMServidores.OSession;
       except
         InterfEntrada.Free;
       end;
 end;

En definitiva, la comparación de un objeto con nil es para saber si existe y si tiene una dirección de memoria asignada.

Hasta luego..;)

Lepe 12-10-2005 14:47:11

Cuidado con ese código, no es standard (lo digo por otro hilo que habla de stardares :D)

el TOracleDataset.Create está dentro del bloque try, esto quiere decir que si se produce una excepción al tiempo de crear ese objeto, irá al bloque except, e intentará liberar un objeto que aún no se ha creado. Además no se usa FreeAndNil en el except, así que de nada nos servirá Assigned.

Código propuesto:
Código Delphi [-]
 procedure TFrmInterfaz.PageControlChange(Sender: TObject);
 begin
   if PageControl.ActivePage <> TabSheet3 then
   begin
     if Assigned(InterfEntrada) then
        //como dice Lepe es lo mismo que if InterfEntrada <> nil
     begin
       DS.DataSet := nil;
       FreeAndNil(InterfEntrada);
     end;
   end
   else
// Si no es distinto, es que es igual, quitamos la pregunta
//     if PageControl.ActivePage = TabSheet3 then
   begin
       InterfEntrada :=  TOracleDataset.Create (nil);
       try
         InterfEntrada.Session := DMServidores.OSession;
       except
         freeandnil(InterfEntrada);
       end;
   end;
 end;

lucasarts_18 12-10-2005 14:57:43

Hola:

Lepe, Sinceramente tienes toda la razón :D.

Saludos..

:cool:

CelestronFan 12-10-2005 19:46:28

Bueno, muchas gracias por la explicación tan detallada.

A ver si estoy en lo correcto, en conclusión nil, Assigned y Freeandnil se usan para corroborar si el objeto sobre el que se va a actuar está creado en memoria y en caso contrario actuar sobre la excepción que se generaría.

Entonces mi pregunta es, en lugar del nil ¿Se puede usar un bloque de este tipo?
Código:

Try 
  sentencias...
 Except
  sentencias...
 Finally o Raise
  sentencias...
 end

Es decir (usando el ejemplo de Lepe) podría hacer algo como esto?:

Código Delphi [-]
  private 
   nodo:TTreeNode;
 end;
 implementation
 
 // inicializamos el valor de nodo
 //procedure TForm1.Form1Create;
 //begin
 //  nodo := nil;
 //end;
 
 // accedemos al nodo o lo creamos si es necesario
 procedure TForm1.button1Click;
 begin
    Try
        nodo.text:= 'si ';
    Except
        nodo := TTreenode.Create(self);
        nodo.text:= 'si ';
  end;
 end;
 
 // al salir liberamos el nodo
 // si tienen nil no hace nada
 // si ha sido creado, lo libera
 
 procedure Tform1.FormClose;
 begin
   freeandnil(nodo);
 end;
La idea no es evitar manejar el nil, sino entender bien como hacerlo y saber si este tipo de casos se puede tratar como una excepción cualquiera.

Aunque me parece que no, porque haría lo indicado para cualquier tipo de excepción y no solo cuando el objeto no exista....:confused:

Si el control que estoy manejando pertenece a la forma principal de mi programa (me refiero a un control normal, que no se cree a posteriori en runtime) entonces no tendría sentido usar el nil, pues todos los controles de la forma se crean y asignan en memoria con Application.CreateForm(...
antes de que delphi ejecute Application.run,

...o ¿es que no he entendido que un control puede estar mostrado en una forma pero no asignado a memoria?

Gracias.

dec 13-10-2005 00:34:48

Hola,


Cita:

Empezado por CelestronFan
(...) en conclusión nil, Assigned y Freeandnil se usan para corroborar si el objeto sobre el que se va a actuar está creado en memoria y en caso contrario actuar sobre la excepción que se generaría.

Bueno. Yo diría que hay que matizar. "nil" puede usarse con los operadores de relación, tal como queda dicho más arriba. "Assigned" puede usarse en sentencias condicionales. "FreeAndNil" sirve para liberar un determinado objeto y hacer que la variable que guardara su referencia no apunte a ningún sitio en concreto. Pero no hay que actuar sobre excepción alguna, en mi opinión, puesto que lo siguiente no tiene porqué generar ninguna excepción.

Código Delphi [-]
 if Objeto <> nil then
   {...}
 else
   {...}
Cita:

Empezado por CelestronFan
Entonces mi pregunta es, en lugar del nil ¿Se puede usar un bloque de este tipo?

Código:

Try
  sentencias...
 Except
  sentencias...
 Finally o Raise
  sentencias...
 end


Creo que de lo de arriba lo que se ve extraño es el "Finaly o Raise". Pudiera ser que estuvieras refiriéndote a "Finally o Except".


Cita:

Empezado por CelestronFan
Es decir (usando el ejemplo de Lepe) podría hacer algo como esto?:

Código Delphi [-]
 // accedemos al nodo o lo creamos si es necesario
 procedure TForm1.button1Click;
 begin
   Try
     nodo.text:= 'si ';
   Except
     nodo := TTreenode.Create(self);
     nodo.text:= 'si ';
   end;
 end;

No es del todo correcto, me parece a mí. Me explicaré. Se intenta asignar determinado valor a cierta propiedad del objeto "nodo": ya aquí, si no estás seguro de que "nodo" esté disponible deberías comprobar con un "if Assigned(nodo) then ..." o un "if nodo <> nil then ..." que esto es así. Si el objeto no estuviera disponible, tal como lo haces más arriba, evidentemente se produciría una excepción, que capturarías, y entonces crearías el objeto "nodo" y asignarías a su propiedad "Text" determinado valor... pero no creo que halla que llevar ahí, puesto que, como digo arriba, bastaría con comprobar si el objeto está creado o no lo está antes de asignar a su propiedad un determinado valor, o crearlo antes y hacerlo luego, si fuera menester.


Cita:

Empezado por CelestronFan
La idea no es evitar manejar el nil, sino entender bien como hacerlo y saber si este tipo de casos se puede tratar como una excepción cualquiera.

Capturar la excepción que se produciría, efectivamente, podrías capturarla, creo yo, pero, ciertamente, creo que lo suyo es hacerlo como arriba se dice, mediante una sentencia condicional en donde se comprobara la disponibilidad del objeto en cuestión.


Cita:

Empezado por CelestronFan
Aunque me parece que no, porque haría lo indicado para cualquier tipo de excepción y no solo cuando el objeto no exista....:confused:

Así es. Pero, además, insisto en que no veo la necesidad de hacerlo de ese modo, pudiéndose hacer como se dice arriba: creo que también se ganaría en legilibidad del código fuente. Y puede que hubiera otras razones por las que no sería recomendable hacer algo así. Se me ocurre que una vez entraras en la excepción... las siguientes instrucciones del procedimiento no se ejecutarían debidamente... aquí creo que me lío un poco y no sé explicarme o no lo tengo del todo claro. Te pido disculpas.


Cita:

Empezado por CelestronFan
Si el control que estoy manejando pertenece a la forma principal de mi programa (me refiero a un control normal, que no se cree a posteriori en runtime) entonces no tendría sentido usar el nil, pues todos los controles de la forma se crean y asignan en memoria con Application.CreateForm(... antes de que delphi ejecute Application.run, ...o ¿es que no he entendido que un control puede estar mostrado en una forma pero no asignado a memoria?

No; has entendido bien, creo yo. Si tú no liberas un control que se muestra en una "forma" "desde su creación", este estará disponible y su referencia también lo estará. Si liberaras el control perderías su referencia y el mismo también dejaría de mostrarse en la "forma". Puedes probarlo. Sitúa dos botones en un formulario y, desde uno de ellos libera al otro: verás cómo desaparece el formulario.

CelestronFan 13-10-2005 01:46:48

Gracias dec y a todos.

Quedó superclaro. Lo que más me cuesta del delphi es cambiar el modo de pensar durante la programación, y eso que me dediqué a hacer una flujograma para mi proyectito pero a pesar de eso me vuelvo un pastel con los objetos a veces. Bueno, con el tiempo y sus excelentes ayudas lo voy a superar....

Muchas Gracias.

Lepe 13-10-2005 14:16:16

Resumiendo sería: "Mas vale prevenir que curar"

Como bien has dicho, la excepción tambien puede ocurrir por otra causa; asumir que se ha producido porque el objeto no esté creado, es demasiado ;)

Cita:

Empezado por CelestronFan
pues todos los controles de la forma se crean y asignan en memoria con Application.CreateForm(...
antes de que delphi ejecute Application.run,

El form Si se crea con Application.CreateForm, por tanto, pertenece a application y application se encargará de liberarlo.

Un TEdit, colocado en el form1, pertenece al Form, por tanto, al cerrar la aplicación:
- Application manda destruir a todos los Forms que contiene.
- Form1 manda a destruir todos los controles que contiene, entre ellos el Edit

De ahí que todos los objetos que son contenedores, TForm, Tpanel, etc, tengan la propiedad Components y ComponentsCount que es una lista de los controles que ha liberar ese objeto, y el orden en el que se crearon.


La franja horaria es GMT +2. Ahora son las 05:40:23.

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