Recent

Author Topic: pregunta sobre liberacion de clases  (Read 9983 times)

zelda16bit

  • Full Member
  • ***
  • Posts: 118
pregunta sobre liberacion de clases
« on: June 14, 2021, 02:42:44 pm »
Hola una vez mas. O:-)

Estoy en el apartado 5 del manual:
https://castle-engine.io/modern_pascal_introduction.html#_freeing_classes

Aqui vienen algunas formas de liberar una clase y me gustaria saber cual es la mejor forma y la mas adecuada.

Otra pregunta seria si yo creo una instancia de una clase en dentro de otra y quiero eliminar esa clase en un metodo con un condicional,¿como se haria?.Elimino la instancia solo en el condicional o en el condicional y en el destructor o como.

Edson

  • Hero Member
  • *****
  • Posts: 1301
Re: pregunta sobre liberacion de clases
« Reply #1 on: June 14, 2021, 06:42:15 pm »
Aqui vienen algunas formas de liberar una clase y me gustaria saber cual es la mejor forma y la mas adecuada.

No hay una forma mejor ni más adecuada, para todos los casos. Depende de cómo manejes tus objetos.

Por ejemplo, si eres cuidadoso y solo creas y destruyes tus objetos una vez y de forma controlada; basta con que uses  el constructor Create() para crear a los objetos y llamar al destructor Destroy() para destruirlos. Esa sería la forma estándar.

En algunos esquemas, como cuando se quieren reusar variables que apuntan a objetos, puede convenir saber si un objeto se ha liberado (si se le ha aplicado un "Destroy"). Para eso se podría usar una variable auxiliar (lo que complicaría el programa), o usar la misma variable del objeto como bandera y asignarle un valor nulo (que en Pascal es NIL) cuando se ha liberado.

Pero llamar a un Destroy() , no pone a NIL la variable del objeto, por eso es que se suele hacer:

Code: Pascal  [Select][+][-]
  1.  
  2.   objeto.Destroy;
  3.   objeto := nil;
  4.  

Luego para evitar liberar objetos ya liberados, se tendría que usar:

Code: Pascal  [Select][+][-]
  1. if A <> nil then begin
  2.   A.Destroy;
  3.   A := nil;
  4. end;

Y como eso es una tarea común, ya existe un procedimiento creado llamado FreeAndNil(). Si tienes más curiosidad sobre FreeAndNil(), puedes ver desde Lazarus, la implementación de ese procedimiento.

También se puede liberar usando objeto.Free, pero hasta donde recuerdo, es similar a Destroy().

Muchos programadores no quieren complicarse y usan a FreeAndNil() para todo. Y eso está bien, funciona. Ellos no quieren ponerse a pensar en qué caso se debe usar FreeAndNil() o Destroy().

Personalmente a mi me gusta usar Destroy() en la mayoría de casos y solo usar FreeAndNil() cuando sea necesario dejar a NIL la variable objeto.

Lazarus 2.2.6 - FPC 3.2.2 - x86_64-win64 on Windows 10

Edson

  • Hero Member
  • *****
  • Posts: 1301
Re: pregunta sobre liberacion de clases
« Reply #2 on: June 14, 2021, 06:49:03 pm »
Otro detalle que hay que tener en cuenta es asegurar que todos los objetos creados sean liberados, para controlar correctamente la memoria usada.

Normalmente, la secuencia formal para la vida de objetos es:

Code: Pascal  [Select][+][-]
  1. objeto := MiClase.Create();
  2. ...
  3. objeto.Destroy()
  4.  

Esa sería la forma más simple y práctica. Pero en algunos casos, cuando estamos dentro de un procedimiento, y usamos instrucciones para salir del procedimiento ante casos de error, corremos el riesgo de no pasar por la parte del Destroy():

Code: Pascal  [Select][+][-]
  1. procedure algun_proc();
  2. begin
  3.   objeto := MiClase.Create();
  4.   ...
  5.   if pasa_algo_malo then exit;
  6.   ...
  7.   objeto.Destroy()  //Puede no ejecutarse
  8. end;
  9.  

Para evitar ese problema es que se suele usar la estructura TRY ... FINALLY ... END:

Code: Pascal  [Select][+][-]
  1. objeto := MiClase.Create();  //Crea al objeto
  2. try
  3.   ...
  4.   if pasa_algo_malo then exit;
  5.   ...
  6. finally
  7.   objeto.Destroy()  //Se ejecuta siempre
  8. end;
  9.  

Es por eso que esta estructura es común y vas a verla en muchos códigos. También funciona en caso en que ocurra alguna excepción dentro del bloque de TRY ... FINALLY, por eso es que es la forma más segura de liberar objetos dentro de subrutinas.
« Last Edit: June 14, 2021, 06:54:14 pm by Edson »
Lazarus 2.2.6 - FPC 3.2.2 - x86_64-win64 on Windows 10

zelda16bit

  • Full Member
  • ***
  • Posts: 118
Re: pregunta sobre liberacion de clases
« Reply #3 on: June 14, 2021, 09:40:00 pm »
Gracias por la respuesta Edson,pero todavia tengo una duda y voy a poner un ejemplo orientativo.

Code: Pascal  [Select][+][-]
  1. type
  2.    Tenemigo = class
  3.    end;
  4.  
  5.   Tjugador = class
  6.      enemigo1: Tenemigo;
  7.      procedure colision();
  8.   end;
  9.  
  10.   procedure Tjugador.colision();
  11.   begin
  12.     if colisiono(self,enemigo1) then
  13.     begin
  14.        enemigo.destroy();
  15.     end;
  16.   end;
  17.  

Bien...en el ejemplo creo una clase enemigo y otra jugador,en la clase jugador creo la instancia de enemigo y en el metodo colision compruebo con un condicional si estoy colisionando con el enemigo y si es asi lo destruyo.

Podrias explicarme de una forma mas detallada cual es la forma correcta de eliminar al enemigo y liberarlo de la memoria.

Edson

  • Hero Member
  • *****
  • Posts: 1301
Re: pregunta sobre liberacion de clases
« Reply #4 on: June 15, 2021, 01:05:08 am »
Tal vez tu ejemplo no sea muy apropiado, porque, por la forma como has definido la clase Tjugador, se entiende que "enemigo" es una parte constituyente de Tjugador, lo cual no es usual en juegos.

Si la pregunta es como destruir objetos creados dentro de objetos. Yo diría, depende cómo quieras adminstrar a los objetos hijos (qué patrón de diseño quieras implementar).

Una forma común es asociar el ciclo de vida de los objetos hijos al objeto padre, en este caso, creamos al objeto(s) hijo(s)  en el constructor y lo destruímos en el destructor:

Code: Pascal  [Select][+][-]
  1. type
  2.   THijo = class
  3.   end;
  4.   TPadre = class
  5.     hijo: THijo;
  6.     constructor Create();
  7.     destructor Destroy();
  8.   end;
  9.  
  10. var
  11.   padre: TPadre;
  12.  
  13. constructor TPadre.Create();
  14. begin
  15.   hijo:= THijo.Create;
  16. end;
  17.  
  18. destructor TPadre.Destroy();
  19. begin
  20.   hijo.Destroy;
  21. end;
  22.  

Pero si quieres crear a los objetos hijos esporádicamente, también lo puedes hacer. Si solo manejas uno, sería simple:

Code: Pascal  [Select][+][-]
  1. type
  2.   THijo = class
  3.   end;
  4.   TPadre = class
  5.     hijo: THijo;
  6.     procedure CrearHijo();
  7.     procedure DestruirHijo();
  8.   end;
  9.  
  10. var
  11.   padre: TPadre;
  12.  
  13. procedure TPadre.CrearHijo();
  14. begin
  15.   hijo:= THijo.Create;
  16. end;
  17.  
  18. procedure TPadre.DestruirHijo();
  19. begin
  20.   hijo.Destroy;
  21. end;

La idea es que siempre se llame a DestruirHijo(), si es que se ha llamado a CrearHijo(). Otra alternativa sería que en el destructor de TPadre se verifique si el atributo "hijo" apunta a un objeto, y de ser así, se llama a Destroy().

Si manejas varios objetos hijo, que se crean y se destruyen aleatoriamente, lo más común es almacenar los objetos en contenedores como arreglos o listas, de modo que cada vez que se crea un objeto se agrega al contenedor y cada vez que se destruye se elimina del contenedor. Esto es común en aplicaciones como editores gráficos o juegos.

Los contenedores favoritos para objetos serían las listas genéricas. Es un poco largo de explicar.

En cualquier caso, se debe asegurar que todos los objetos creados sean destruídos. Eso es responsabilidad del programador.
« Last Edit: June 15, 2021, 01:07:38 am by Edson »
Lazarus 2.2.6 - FPC 3.2.2 - x86_64-win64 on Windows 10

zelda16bit

  • Full Member
  • ***
  • Posts: 118
Re: pregunta sobre liberacion de clases
« Reply #5 on: June 15, 2021, 02:42:06 pm »
Gracias por la ayuda Edson. ;)

Ñuño_Martínez

  • Hero Member
  • *****
  • Posts: 1186
    • Burdjia
Re: pregunta sobre liberacion de clases
« Reply #6 on: June 19, 2021, 02:08:26 pm »
A ver:

¡NUNCA LLAMÉIS AL MÉTODO DESTROY!

Ese método, aunque sea público, no está diseñado para ser llamado de forma directa.  Para destruir un objeto hay que llamar al método Free.

Code: Pascal  [Select][+][-]
  1.   procedure TJugador.Colision;
  2.   begin
  3.     if Colisiono (Self, Enemigo) then
  4.       Enemigo.Free
  5.   end;
  6.  

En otros casos puede ser mejor usar el procedimiento FreeAndNil, para asegurarse de que la variable de referencia contendrá Nil una vez destruido.

Code: Pascal  [Select][+][-]
  1.   procedure TJugador.Colision;
  2.   begin
  3.     if Colisiono (Self, Enemigo) then
  4.       FreeAndNil (Enemigo)
  5.   end;
  6.  

Ya sé que existe la tentación de llamar directamente a Destroy para ahorrarse una llamada, pero se trata de un patrón de diseño que se decidió en su momento y es útil.
Are you interested in game programming? Join the Pascal Game Development community!
Also visit the Game Development Portal

zelda16bit

  • Full Member
  • ***
  • Posts: 118
Re: pregunta sobre liberacion de clases
« Reply #7 on: June 19, 2021, 09:29:28 pm »
Gracias por el consejo Ñuño_martinez. ;)

Aunque si cada una dice una cosa me hago un lio %),por cierto...puedes darme algun consejo sobre la creacion y liberacion de objetos con TObjectList,si puedes mejor en su propio hilo donde hice la pregunta.

440bx

  • Hero Member
  • *****
  • Posts: 3944
Re: pregunta sobre liberacion de clases
« Reply #8 on: June 20, 2021, 01:46:57 am »
¡NUNCA LLAMÉIS AL MÉTODO DESTROY!
never use big red letters
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

Edson

  • Hero Member
  • *****
  • Posts: 1301
Re: pregunta sobre liberacion de clases
« Reply #9 on: June 20, 2021, 05:37:44 am »
¡NUNCA LLAMÉIS AL MÉTODO DESTROY!

Ese método, aunque sea público, no está diseñado para ser llamado de forma directa.  Para destruir un objeto hay que llamar al método Free.

Alguien que me explique por qué no debo llamar a Destroy() y sí a Free(). Tengo varios años programando, cientos de programas y miles de llamadas a Destroy(), sin tener ningún problema. Tal vez he vivido engañado toda mi vida.  :o
Lazarus 2.2.6 - FPC 3.2.2 - x86_64-win64 on Windows 10

lucamar

  • Hero Member
  • *****
  • Posts: 4219
Re: pregunta sobre liberacion de clases
« Reply #10 on: June 20, 2021, 06:16:39 am »
Con la mayoría de clases (si no con todas) no importa demasiado que llames a Free() o directamente al destructor Destroy. Después de todo lo usual es que Free haga nada más que:
Code: Pascal  [Select][+][-]
  1. procedure TObject.Free;
  2. begin
  3.   // the call via self avoids a warning
  4.   if self<>nil then
  5.     self.destroy;
  6. end;
y dado que no es un método virtual prácticamente nadie lo reintroduce para que haga otra cosa; en todo caso, lo usual es delegar cualquier otra tarea para el destructor (Destroy), que sí es virtual.

Usar Free en vez de Destroy se convierte entonces en algo casi puramente convencional y más que nada "defensivo", casi tanto como la usual (y altamente redundante*) secuencia:
Code: Pascal  [Select][+][-]
  1. if Assigned(SomeObject) then SomeObject.Free;

Y por supuesto algo tan convencional, y aunque tenga cierto sentido "defensivo" testear contra Nil antes de llamar al destructor, no amerita enormes letras rojas como si fuera una prohibición divina ;D


* Por clarificar un poco más la redundancia, esa secuencia se convierte en última instancia en algo así como:
Code: Pascal  [Select][+][-]
  1. if SomeObject <> Nil then {if Assigned(SomeObject) then}
  2.   {SomeObject.Free;}
  3.   if SomeObject <> Nil then
  4.     SomeObject.Destroy;
Y aún así es una secuencia muy, muy usada (incluso por mí mismo) :D
« Last Edit: June 20, 2021, 06:27:17 am by lucamar »
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus/FPC 2.0.8/3.0.4 & 2.0.12/3.2.0 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

Ñuño_Martínez

  • Hero Member
  • *****
  • Posts: 1186
    • Burdjia
Re: pregunta sobre liberacion de clases
« Reply #11 on: July 03, 2021, 05:52:37 pm »
Demás de lo explicado por Lucamar, existe otra razón.  Es posible reintroducir el método Free.  Sí, es raro, muy raro, pero se puede hacer y puede dar lugar a efectos laterales o fallos de funcionamiento difíciles de ver.

¡NUNCA LLAMÉIS AL MÉTODO DESTROY!
never use big red letters
Scuse me :-[
Are you interested in game programming? Join the Pascal Game Development community!
Also visit the Game Development Portal

lucamar

  • Hero Member
  • *****
  • Posts: 4219
Re: pregunta sobre liberacion de clases
« Reply #12 on: July 03, 2021, 06:23:06 pm »
Es posible reintroducir el método Free.  Sí, es raro, muy raro, pero se puede hacer y puede dar lugar a efectos laterales o fallos de funcionamiento difíciles de ver.

Sí, puedes reintroducirlo pero EMHO eso debería considerarse muy mala práctica.

Lo único que puedo pensar que uno podría hacer en un Free reintroducido que no debería, y de hecho no puede, hacerse en Destroy es llamar condicionalmente al destructor, como en:
Code: Pascal  [Select][+][-]
  1. procedure TSomeClass.Free;
  2. begin
  3.   if SomeCondtion and (self<>nil) then
  4.     self.destroy;
  5. end;
pero incluso eso debería hacerse de otra manera, de modo que lo que sea condicional sea llamar a Free (o Destroy) desde el código usuario de la clase, precisamente por el problema que apuntas: reintroducir y añadir un comportamiento extraño o con efectos colaterales en Free puede llevar a bugs y errores difíciles de depurar, debido principalmente a que uno no lo espera: lo normal es que llamar a Free o Destroy sea más o menos equivalente y cualquier otra cosa parecerá una "trampa para incautos" ;)
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus/FPC 2.0.8/3.0.4 & 2.0.12/3.2.0 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

 

TinyPortal © 2005-2018