Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   OOP (https://www.clubdelphi.com/foros/forumdisplay.php?f=5)
-   -   Como libero una forma que llame con un show (https://www.clubdelphi.com/foros/showthread.php?t=21216)

jaaron 10-05-2005 18:47:53

Como libero una forma que llame con un show
 
Tengo un problema que posiblemente sea muy basico pero es que normalmente muestro las formas de forma modal y al final las elimino con un free, pero estoy programando una aplicacion en la cual necesito crear y mostrar las formas no-modales para que el usuario pueda cambiar de una forma a otra, el problema es que la aplicacion va a estar creando y eliminando las formas de forma dinamica por lo que me interesa saber como las mando eliminar ya que al ponerle el free y al mandarlas crear, me toma como si ya estuvieran creadas y se salta a otras partes del codigo

Código Delphi [-]
  procedure TFmenuprincipal.BPRECIOSClick(Sender: TObject);
  begin
     if not(Assigned(Fmenuprecios)) then  //se checa para no volverla a crear
        Fmenuprecios:=TFmenuprecios.Create(Self);
  
     Fmenuprecios.Show;
     Fmenuprecios.SetFocus;            
     Fmenuprecios.Pmenu.Left:=0;
     Fmenuprecios.Pmenu.Height:=50;
  end;

Este es el codigo con el que creo la forma, y pues ya en el OnClose de la forma que mande llamar esta este codigo:

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

la cuestion es que cuando se intenta volver a crear ya no entra al Create toma como si ya estuviera creada y se salta al Show y me manda este error:

"Project Project1.exe raised exception class EAccessViolation with message 'Access violation at address 00000000. Read of address 0000000000. Process stopped. Use Step or Run to continue."

quiero suponer que es porque no libera la forma completamente y no vuelve a crear una nueva y cuando cae al Show o donde le mando las corrdenadas no hay nada creado, o no se, ahi si alguien me puede echar la mano se los agradezco, se que la opcion mas facil seria ocultar la forma, pero se van a crear muchas formas por lo que si necesito estarlas creando y destruyendo, de antemano gracias.

roman 10-05-2005 19:55:48

No es que no libere completamente el formulario. Recuerda que una variable de tipo objeto es en realidad un puntero al objeto. Al usar Free se libera la memoria asignada al objeto pero el puntero sigue apuntando a una porción de memoria que ya no es válida. En algún momento tienes que volver a poner el puntero en nil. Una comparación como

if Assigned (FMenuPrecios) then

realmente no verifica si el objeto existe o no, únicamente verifica si la referencia a dicho objeto (la variable) es nil o no. Es decir, la comparación es equivalente a:

if FMenuPrecios <> nil then

En el evento OnClose podrías intentar poner

FMenuPrecios := nil;

// Saludos

dec 10-05-2005 19:57:54

A ver qué tal parece esta opción
 
Hola,

Donde dije:

"Yo lo he podido solucionar de esta manera, que, probablemente no sea la mejor. En todo caso a ver qué te parece la solución y la explicación que le doy al error que te encuentras."

Debí decir:

Vale. Hasta aquí el típico párrafo en que uno se lava las manos antes de tratar de indicar la solución de un problema, comprendiendo que lo que va a decir puede estar equivocado (como así ha resultado).

Donde dije:

"Primero el error. Este aparece, si no estoy equivocado, porque, efectivamente, al cerrar una vez la "forma" secundaria liberas de la memoria cualquier referencia de esta."

Debí decir:

Hasta aquí más o menos estoy de acuerdo con lo que pensaba antes. Efectivamente, liberas una referencia, pero no cualquier referencia; y no es que se libere la "forma", pero únicamente una referencia a la misma.

Donde dije:

"Luego, al comprobar que no está creada la forma secundaria para crearla de nuevo se produce un error, porque, no está creada, pero tampoco puede crearse, puesto que su tipo ya no existe en la aplicación: ya se liberó."

Debí decir:

Esto es de lo peor, creo. Como bien dice roman, no se comprueba (con "if not Assigned(Form2) then ...") si la forma ha sido o no creada, sino si la referencia a ella es nula ("nil"). Y el tipo de la forma no puede liberarse: puede liberarse una determinada referencia al tipo, o un objeto de dicho tipo, pero no el tipo como tal.

Donde dije:

"La solución puede que pase por declarar una variable del tipo de la forma en donde vayas a emplear esta. Comprobar que no está ya creada una instancia de esta forma (secundaria) en la variable en cuestión. ¿No lo está? Se crea y se muestra la forma. ¿Lo está? Entonces no hace falta mostrarla, sino darla el foco, si es preciso."

Quise decir:

La solución no pasa por ahí, ya está claro, pero, ¿cómo pude pensar lo contrario? En realidad no hay que crear una variable del tipo de la forma, porque dicha variable ya está creada, quiere decirse, podemos utilizarla. Esta variable no guarda la forma como tal, sino una referencia a ella. Lo que debemos comprobar, como dice roman, es que dicha variable no guarda una referencia nula.

Donde dije:

"Utilizamos el evento "OnClose" de la forma secundaria para dar el valor "caFree" a la variable "Action" de tipo "TCloseAction"."

Debí decir:

Vamos a ver... ¿qué pretendía conseguir con esto? Efectivamente, dando el valor "caFree" a la variable "Action" que podemos encontrar en los parámetros del evento "OnClose" de un formulario, ¿qué liberamos? ¿Liberamos la referencia a dicho formulario? ¿El propio formulario? ¿El tipo del formulario? Liberamos la memoria asignada al objeto del tipo del formulario, pero no la referencia a este, la cual seguirá estando disponible... aunque ya no nos servirá, pues esta referencia será nula.

Donde dije:

"Aquí puedes ver un poco de código:"

Código Delphi [-]
       uses
         Unit2; // Contiene el tipo de una "Forma" secundaria
       
       var // Esta variable contendrá dicho tipo
         Formulario2: TForm2; 
       
       procedure TForm1.Button1Click(Sender: TObject);
       begin
         // Si la forma secundaria no se ha asignado aún
         if not Assigned(Form2) then
         begin
           // Utilizamos "Formulario2" para crear la Forma secundaria
           Formulario2 := TForm2.Create(Self);
           Formulario2.Show; // Y la mostramos
         end else // Si la forma secundaria ya ha sido asignada
           Formulario2.SetFocus; // Enfocamos
       end;

Debí decir:

Aquí está el código tanto de la parte en que se crea el formulario o forma como de la sentencia a utilizar en el evento "OnClose" de dicho formulario:

Código Delphi [-]
  
  (* Desde Form1 mostraremos el formulario o forma Form2 *)
  
  uses
    Unit2; // Contiene TForm2
  
  procedure TForm1.Button1Click(Sender: TObject);
  begin
     If not Assigned(Form2) then // Podría ser: if Form2 = nil then
     begin   
       Form2 := TForm2.Create(Self);
       Form2.Show();
     end else
       Form2.SetFocus();
  end;
  
  (* Evento OnClose de Form2 *)
  
  procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
  begin
    Form2 := nil; // Anulamos la referencia a Form2 existente hasta aquí
  end;

Donde dije:

"No creas que me sé explicar todo lo que trato de explicarte a ti (¿Qué es esto?), al menos no todavía, pero el caso es que funcionar parece funcionar. Pruébalo tú a ver qué tal."

Debí decir:

No tengo mucha idea de cómo lo he hecho, pero, tecleando como un poseso he conseguido solucionar el problema, al menos, aparentemente. Tal vez alguien pueda aportar otro punto de vista, pero, tal vez esto pueda ayudarte en algo.

dec 10-05-2005 20:05:00

Pues me adelanté completamente
 
Hola,

Lo cierto es que me parecía haber probado a asignar el valor "nil" a la "forma" secundaria en su evento "OnClose". Juraría que lo hize, pero, no puede ser, otra cosa haría y me he confundido.

Efectivamente, roman lleva razón: con darle el valor "nil" en el evento "OnClose" a la forma secundaria el problema se resuelve igualmente, bueno, igualmente no, de una manera más elegante que con "mi solución".

Y además de que mi solución es menos elegante es que no está basada en la teoría que expone german... sino en no sé qué teoría... que así ha salido.

Quiero decir que no comprendo aún muy bien ni lo que ha expuesto roman ni lo que yo mismo he expuesto: tendré que releer los comentarios para tratar de hacerlo. No sé cuántas veces.

Actualización: No he querido borrar el comentario anterior en que trato de aportar una solución al problema aunque dicha solución no fuera acertada del todo, al menos.

He preferido editarlo, de tal modo que se pueda leer en los apartados "Donde dije:" el comentario tal como lo realizé originalmente, y, en los apartados "Debí decir:" lo que, después de leer a roman y tratar de comprender su solución (que es la correcta) he podido comprender y valga la redundancia.

jaaron 10-05-2005 20:47:45

Pues efectivamente, con el nil se soluciona
 
Como puse en el titulo, ya con el nil se soluciona, de hecho comprobe que crea una forma nueva cada vez que pulso el boton que manda crearla, ya decia yo que iba a ser algo sencillo pero que sino fuera por ustedes me hubiera costado mucho trabajo solucionar, una agradecimiento a ambos tanto por su ayuda y su rapida contestacion, es bueno saber que se puede contar con ayuda cuando se necesite, gracias a ambos. :p

Ohcan 11-05-2005 10:17:06

FreeAndNil
 
De la ayuda de Delphi:

Cita:

FreeAndNil procedure

Frees an object reference and replaces the reference with nil (Delphi) or NULL (C++).
Unit
SysUtils
Category
Pointer and address routines
Delphi syntax:
procedure FreeAndNil(var Obj);
C++ syntax:
extern PACKAGE void __fastcall FreeAndNil(void *Obj);
Description
Use FreeAndNil to ensure that a variable is nil (Delphi) or NULL (C++) after you free the object it references. Pass any variable that represents an object as the Obj parameter.
Warning: Obj must be an instance of a TObject descendant.
Entonces... ¿no sería mejor usar siempre ésto en vez del .free?

jaaron 09-06-2005 19:39:52

No estoy seguro si libera completamente
 
Pues soy yo de nuevo con una nueva duda, ya decia yo que era mucha buena suerte que todo saliera bien, el problema que tengo es este:

Como les habia dicho creo la forma de manera dinamica y la muestro con un Show, el primer problema era que no sabia como liberar esa forma, gracias a la ayuda anterior probe con Nil y parecia que todo iba bien, pero....por cuestiones de programacion necesite asignarle el nombre de la forma despues de crear, el problema esta que al crear la forma la primera vez todo funciona bien, la cuestion es que cuando salgo de la forma y la libero con nil, al querer volverla a crear me dice que el objeto con el nombre que asigno ya existe, ahora mi duda, es si realmente la orden Nil libera totalmente la memoria o necesito de algo mas?

Código Delphi [-]
          if not(Assigned(Fpre_captura)) then
          begin
             Fpre_captura:=TFpre_captura.Create(Self);
             Fpre_captura.Name:='Fpre_captura';
         end;     
         Fpre_captura.Show;

y para liberar en el OnClose pongo esto:

Código Delphi [-]
        Fpre_captura:=Nil;

y como no estaba seguro de que nombre creaba, intente quitarle la asignacion del nombre y ver que nombre ponia la segunda vez que se creaba la forma y el nombre que se crea es este: Fpre_captura_1, entonces eso quiere decir que la primera forma todavia se queda en memoria o hace falta algo mas por liberar, ya intente usar freeandnil pero me manda un error de memoria y me cierra la aplicacion, les agradezco su ayuda de antemano.


La franja horaria es GMT +2. Ahora son las 16:56:37.

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