Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   OOP (https://www.clubdelphi.com/foros/forumdisplay.php?f=5)
-   -   liberar objetos (https://www.clubdelphi.com/foros/showthread.php?t=25845)

OscarG 05-10-2005 17:57:49

liberar objetos
 
Bueno, por mi parte como programador de seudo videojuegos (no hago quakes ni na parecido jeje) uno de mis miedos el liberar la memoria, ya sea de punteros y objetos...

Algunas cosas he leido sobre cómo liberar los objetos pero a veces tengo problemas q no encuentro solución.

Un ejemplo era con la clase TServerSocket q lo utilicé hace tiempo para un juego en red.

El problema era q a la hora de liberar dicho objeto, me daba error, no me dejaba hacer un .Free por lo q opté por la decisión de no liberarlo.

Supongo q si aprendo a liberar un TServerSocket, con otros problemas q me tope, tendré más oportunidades de solucionarlo.

En todo caso por lo q sé...todo objeto se libera con Free y yo lo suelo poner luego a nil porsiaca...

Neftali [Germán.Estévez] 05-10-2005 18:04:08

Cita:

Empezado por OscarG
...todo objeto se libera con Free y yo lo suelo poner luego a nil porsiaca...

Para eso te puede ser últi el procedimiento FreeAndNil (SysUtils en D6).
Creo que es obvio lo que hace... :D

lento manu 05-10-2005 18:15:16

Ejecutar o indultar
 
Por lo que voy entendiendo, creo que solo hay que liberar los objetos que creamos espeficicamente. Quiero decir que si usamos objetos que hemos añadido al private o al public y no presisan de un TmiObjeto.create, por mi experiencia, creo que es mejor el indulto, pero todos los que has generado con algún .create deben pasar por las manos del verdugo.
Lo que no se es porque algunos objetos existen por si mismos y otros no, no se si es por su componente visual, o sea si debe ser pintado o no... ¿alguén los sabe? o ¿estoy metiendo la pata hasta el fondo?
Con respecto al
Código Delphi [-]
 if Assigned(miobjeto) 
    then FreeAndNil(miobjeto);
a veces me explota ya en if Assigned pq ya no existe. ¿Ilógico no?

Lepe 05-10-2005 20:08:49

Cita:

Empezado por lento manu
Lo que no se es porque algunos objetos existen por si mismos y otros no, no se si es por su componente visual, o sea si debe ser pintado o no... ¿alguén los sabe? o ¿estoy metiendo la pata hasta el fondo?

Al crear un objeto Pepe:TObject tienes que especificar el dueño (parámetro AOwner), si le pones nil a ese parámetro, tienes que hacer un FreeAndNil(Pepe) obligatoriamente. Si en AOwner le das: Application, Form1, panel1, cuando se vaya a destruir ese objeto, él se encargará de liberar a Pepe.
Cita:

Empezado por lento manu
Con respecto al
Código Delphi [-]
 if Assigned(miobjeto) 
    then FreeAndNil(miobjeto);
a veces me explota ya en if Assigned pq ya no existe. ¿Ilógico no?

[/quote]
Si miobjeto está declarado dentro de Form1, y has liberado Form1, entonces si da el error.

Assigned solo comprueba que el valor de esa variable contiene el valor "nil" o bien una dirección de memoria. Recuerda que todos los objetos son, en realidad, punteros de memoria. Assigned solo comprueba el valor que tiene ese puntero. No el objeto en sí.

De hecho, cuando haces un Pepe.Free, el puntero Pepe no apunta a nil, se queda con basura y pueden venir Access violation al intentar acceder a pepe.

Saludando, dejo tranquilo a Pepe ;)

OscarG 06-10-2005 12:17:55

Bueno, conozco el FreeAndNil, pero no lo suelo usar...manias...

Lo q quiero decir con crear, viendo lo comentado y según me acuerdo, (es un programa q hice hace 1 año) es q yo creaba un cliente y un servidor.

TServerSocket
TClientSocket

El procedimiento para cada uno era tener una clase por cada y dentro de ella declarar un servidor o un cliente y q esa clase le controle su comportamiento...

Entonces, a estos les pasaba al crearlos el TComponent del formulario.

Entonces al destruir la clase q contenía el TServerSocket no me dejaba hacer un FreeAndNil del objeto, en cambio, con el TClientSocket si me dejaba...

Creo recordar q me comí un poco la cabeza probando formas diferentes para q no cascase, pero al final opté por q lo libere la aplicación al destruirse, ya q dependía del formulario principal...

Este caso, me mosqueo, xq me parecía q hacia todo bien

Código Delphi [-]
SSocket:= TServerSocket.Create(AOwner);
 SSocket.Port := 9998;
 SSocket.Active := false;
 SSocket.ServerType:= stNonBlocking;
 SSocket.ThreadCacheSize:= 10;
 SSocket.OnClientRead := ServerSocketClientRead;
 SSocket.OnClientError := ServerSocketClientError;
 SSocket.OnClientConnect := ServerSocketClientConnect;
 SSocket.OnClientDisconnect:= ServerSocketClientDisconnect;
pero no me dejaba luego liberarlo, al hacerlo me salía un error.

Pos eso, muchas gracias...

OscarG 06-10-2005 12:23:22

Bueno, conozco el FreeAndNil, pero no lo suelo usar...manias...

Lo q quiero decir con crear, viendo lo comentado y según me acuerdo, (es un programa q hice hace 1 año) es q yo creaba un cliente y un servidor.

TServerSocket
TClientSocket

El procedimiento para cada uno era tener una clase por cada y dentro de ella declarar un servidor o un cliente y q esa clase le controle su comportamiento...

Entonces, a estos les pasaba al crearlos el TComponent del formulario.

Entonces al destruir la clase q contenía el TServerSocket no me dejaba hacer un FreeAndNil del objeto, en cambio, con el TClientSocket si me dejaba...

Creo recordar q me comí un poco la cabeza probando formas diferentes para q no cascase, pero al final opté por q lo libere la aplicación al destruirse, ya q dependía del formulario principal...

Este caso, me mosqueo, xq me parecía q hacia todo bien
Código Delphi [-]
  SSocket:= TServerSocket.Create(AOwner);
  SSocket.Port :=               9998;
  SSocket.Active :=             false;
  SSocket.ServerType:=          stNonBlocking;
  SSocket.ThreadCacheSize:=     10;
  SSocket.OnClientRead :=         ServerSocketClientRead;
  SSocket.OnClientError :=         ServerSocketClientError;
  SSocket.OnClientConnect :=    ServerSocketClientConnect;
  SSocket.OnClientDisconnect:=  ServerSocketClientDisconnect;

pero no me dejaba luego liberarlo, al hacerlo me salía un error.

Pos eso, muchas gracias...

Lepe 06-10-2005 12:48:36

Explicación detallada:

Tu hacias un .Free del SSocket, se libera pero no queda apuntando a nil el valor de SSocket, por tanto, cuando el Aowner se destruye, accede a SSocket, ve que es distinto de nil, e intenta liberar una zona de memoria que ya ha sido liberada => Access Violation.

Si hubieras usado Freeandnil(SSocket), cuando el Aowner va a destruirlo con .Free, se detecta que SSocket es = nil, y por tanto, no se destruye, No hay Access Violation.

Moraleja: Haz un curso de autoControl y quitate la manía que tienes con FreeAndNil ;)

Un saludo

OscarG 06-10-2005 15:40:03

Que yo sepa, FreeAndNil es lo mismo q poner un objeto Free y luego igualarlo a nil. Ya se q son 2 pasos en vez de uno pero creo q són lo mismo.

Me parece extraño q no lo haya probado, más bien diría imposible, (no poner FreeAndNil, sino, ponerlos por separados, Free y luego nil...) en el ejemplo del TServerSocket.

Si evito poner FreeAndNil es xq en mis inicios me dio algunos problemas q resolví quitandolo...seguramente sería por ser novato y si algo me da problemas he aprendido a evitarlos y buscar otra forma de hacerlo.

Hoy pienso q sería por otra cosa pero aún asi, intento evitarlo porsiaca.

He cargado una prueba q tenía de cuanto investigé los TServerSocket, y le he puesto FreeAndNil y casca, al igual q poner Free y luego Nil...en la prueba lo he puesto en el FormDestroy, luego lo he probado con un botón para crear un ServerSocket y otro para eliminarlo y me ha funcionado...asi q debe ser por ponerlo en el FormDestroy.
¿Xq´ casca en el Destroy?
Ya q si tengo el método para eliminarlo en el Destroy pero anteriormente con el botón q creé para eliminar el Server lo pulso, entonces no casca (ya q en el destroy miro si está asignado), sólo casca si está creado el ServerSocket y lo libero en el Destroy...

Lepe 06-10-2005 16:13:37

Código Delphi [-]
procedure FreeAndNil(var Obj);
var
  Temp: TObject;
begin
  Temp := TObject(Obj);
  Pointer(Obj) := nil;
  Temp.Free;
end;

como ves se hace al revés ¿Por qué? Porque puede ocurrir alguna excepción al tiempo de liberar el objeto, y entonces no quedaría apuntando a nil, y el peligro sería el mismo.

En cuanto a lo demás, sería mejor que pegaras el código que te da error; ya que tengo mis dudas en eso de usar FormDestroy, ¿no querrás decir FormClose?

De hecho nunca se debe llamar a destroy, siempre se debe usar .Free sencillamente por esto:
Código Delphi [-]
procedure TObject.Free;
begin
  if Self <> nil then
    Destroy;
end;

Si primero se usa FreeAndnil, la siguiente llamada a .Free no causará daño alguno.

Si se usa FreeAndNil y despues .Destroy, seguro que casca porque Destroy no comprueba el valor de nil.

Un saludo

Un saludo

adlfv 06-10-2005 17:43:12

Hola a todos, yo creo que tengo el mismo error... y me estoy volviendo LOOOOCO y no logro resolverlo. Seguramente sea una tontería, pero es que NO LO LOGRO.

Explico mi caso.

Estoy haciendo el modelo de negocios de un hotel, el cual explico brevemente:
- Clase THabitaciones (hereda de TComponent), que es básicamente una lista con componentes THabitacion (o TBaseHabitacion).
- Clase THabitacion (hereda de TComponent), representa una habitación como tal. Está formada basicamente por dos partes, Un componente GUI: TBaseGUI y un objeto Info: TBaseInfo.
- Clase TBaseGUI (hereda de TComponent), es el tope de una jerarquia para la representación gráfica de las habitaciones.
- Clase TBaseInfo (hereda de TObject), es el tope de una jerarquia para la información de las habitaciones. Va variando según el tipo en tiempo de ejecución.


En el form principal hago lo siguiente en el evento OnClose:
Código:


 ...
 TiposHabitaciones.Free;
 Habitaciones.Free;


La idea es:
1.- Destruir Habitaciones: THabitaciones (cuando creo una Habitacion: THabitacion hereda de TComponent y el Owner es Habitaciones, con lo cual automáticamente se liberan todas las THabitacion cuyo Owner sea Habitaciones).
2.- Cuando se destruye una Habitacion: THabitacion, básicamente tengo que destruir la Info pues hereda de TBaseInfo que a su vez hereda de TObject. GUI no hay que destruirlo, pues se autodestruye porque tiene como Owner la Habitacion
3.- Cuando se destruye un GUI: TGUIHab, debo liberar el bitmap.


A continuación pondré los métodos de destrucción de las clases en ese orden:


1.- Destrucción de Habitaciones: (en realidad no hago nada salvo Lista.Clear y Lista.Free).

Código:

Debug('THabitaciones.Destroy: ComponentCount:' + IntToStr(ComponentCount));
 Debug('THabitaciones.Destroy: FLista.Count:' + IntToStr(FLista.Count));
 
 for i := 0 to FLista.Count -1 do
 begin
  H := ItemsByPos[i];
  if Assigned(H) then
  begin
        if H.Owner <> nil then
          Debug(H.Caption + ' Owned by: ' + TComponent(H.Owner).Name);
 //        H.FreeInfo;
 //        H.FreeGUI;
 //        H.Free;
  end;
 //  Lista.Objects[i].Free;
 end;
 
 Lista.Clear;
 Debug('THabitaciones.Destroy: ComponentCount:' + IntToStr(ComponentCount));
 
 Lista.Free;
 Debug('La lista interna ha sido liberada');
 inherited Destroy;


2.- Destrucción de Habitacion: Tengo qeu destruir sólo la Info (pero está comentado, pues sospecho que me da error en el Assigned(Info)) pues el GUI tiene como Owner la Habitación y se destruirá solo.


Código:

Debug(Caption + ': THabitacion.Destroy: ComponentCount:' + IntToStr(ComponentCount));
 
 if Assigned(Info) then
 begin
  Debug(Caption + ': TBaseHab.Destroy - assignado. Tipo:' + Info.ClassName);
 //  FreeInfo;
 //  FreeGUI;
 end
 else
  Debug(Caption + ': TBaseHab.Destroy - no assignado');
 
 Debug(Caption + ': THabitacion.Destroy: ComponentCount:' + IntToStr(ComponentCount));
 inherited Destroy


3.- Destrucción de GUI.


Código:


 if Assigned(Owner) then
 begin
  Debug('Liberando bitmap de GUI de la habitacion: ' + TComponent(Owner).Name);
  FreeResources;
 end;
 
 Debug('TGUIHabIcon: ComponentCount=' + IntToStr(ComponentCount));
 
 //TODO: LLamar al heredado?


La destrucción de Info no hace nada salvo llamar al heredado.


Al cerrar la aplicación obtengo un precioso mensaje :mad:: "Invalid pointer operation." (EInvalidPointer)

Alguien me puede echar una mano con esto, por favor.

Le estaré muy agradecido, pues no logro detectar el error... Creo que falla en el if Assigned(Info) de THabitacion.Destroy, pero no estoy seguro y además no sé cómo resolverlo.

Muchas gracias de antemano y disculpen por el mensaje tan largo.

PD: A continuación adjunto la pila, por si pueda ser útil.

------------------------------------------------------------------------------------------------
|Address |Module |Unit |Class |Procedure/Method |Line|
------------------------------------------------------------------------------------------------
|40003364|rtl90.bpl |System.pas | |Error | |
|400031AB|rtl90.bpl |System.pas | |_FreeMem | |
|40003198|rtl90.bpl |System.pas | |_FreeMem | |
|40005DBE|rtl90.bpl |System.pas | |_ClassDestroy | |
|40005DBC|rtl90.bpl |System.pas | |_ClassDestroy | |
|01658F68|CoreClassHab.bpl|UCGUIHab.pas |TGUIIcon |Destroy |127 |
|4003D718|rtl90.bpl |Classes.pas |TComponent |DestroyComponents | |
|4003D518|rtl90.bpl |Classes.pas | |TComponent | |
|01654CD6|CoreClassHab.bpl|UCHabitaciones.pas|TBaseHabitacion|Destroy |195 |
|77D2F3DE|user32.dll | | |SendMessageA | |
|77D2F39A|user32.dll | | |SendMessageA | |
|0067455A|vcl90.bpl |Comctrls.pas |TCustomListView|Scroll | |
|40006B58|rtl90.bpl |System.pas | |_LStrArrayClr | |
|003F203E|CoreLog.bpl |ULog.pas | |Log |64 |
|4003D718|rtl90.bpl |Classes.pas |TComponent |DestroyComponents | |
|4003D518|rtl90.bpl |Classes.pas | |TComponent | |
|01655442|CoreClassHab.bpl|UCHabitaciones.pas|THabitaciones |Destroy |429 |
|400059D4|rtl90.bpl |System.pas |TObject |Free | |
|400059CC|rtl90.bpl |System.pas |TObject |Free | |
|01654433|CoreClassHab.bpl|UClassObjects.pas | |CoreClassHabDestroy |30 |
|01654424|CoreClassHab.bpl|UClassObjects.pas | |CoreClassHabDestroy |29 |
|01715794|CoreMain.bpl |UPrincipal.pas |TFrmPrincipal |FormClose |357 |


lento manu 06-10-2005 17:44:33

gracias por las aclaraciones
 
Gracias por tus aclaraciones, Lepe. Pero es un tema difícil de seguir, ya que realmente el compilador explota, y cuesta mucho encontrar el punto donde sucede esta explosión. He tenido este problema en el componente que discutimos en el hilo Override eventos On... (http://www.clubdelphi.com/foros/showthread.php?t=25540)
Si en el destroy del objeto repaso si debo destruir el objeto creado con
Código Delphi [-]
destructor TDBexplorer.Destroy;
begin
  if Modal then
    begin
      //no destruir si no es modal: error de assigned
      if Assigned(Explorador) then
        FreeAndNil(Explorador);
   end;
  inherited Destroy;
end;
he concluido lo que describo en el comentario //... Lo que quiere decir que si el Tform creado esta generado como no Modal, se ha liberado automáticamente por si mismo, por lo menos su puntero ¿no?, pero si es Modal si pasa por el código y realiza el FreeAndNil(Explorador). Depurando es muy difícil de seguir esto, ya que se reciben los típicos avisos de Access Violation Address con matrículas que a los mortales no nos dicen nada, y cuesta encontrar en que línea de código sucede, ya que el compilador no la muestra por defecto. Para mi, creo que este es el gran defecto de Delphi, por lo menos su talón de Aquiles.Y entiendo la opción de descartar estas llamadas, por impotencia. Actualmente tengo estos errores al cerrar un proyecto, no se cierra adecuadamente y tengo Access Violation Address ...in module rtl60.dpl se cuelga Delphi simplemente por abrir y cerrar el proyecto, pero no por depurarlo ¿...? incluso el exe rueda OK y no produce errores. Por supuesto cuando sale la ventana de las direcciones de memoria del compilador, para mi es peor que el chino, árabe y japonés todo junto. Si tenéis alguna pista, ...gracias a todos.

OscarG 06-10-2005 18:17:40

Me he quedado de piedra...
Si q tienes razón q tiene q ser en el close...el caso es q lo he puesto en el FormClose y parece q ya no casca...menudo despiste :eek: . Aún asi investigaré en la aplicación q me daba el problema... xq esto era un ejemplo y en una aplicación, me corto las venas si veo q pongo algo en el Destroy..., si encuentro algo raro lo comentaré en este hilo.

Por lo otro lado, también admito mi error en lo de FreeAndNil, a partir de ahora le tendré más respeto a dicho método.

Bueno, hoy por lo menos he aprendido cosas...

Otra preguntita q me ha salido hoy sobre liberar...


Estoy con un componente de música q me he bajado, el ACS
Audio Components Suite
para ulilizar oggs, la cosa es q quiero cerrar el sonido y liberarlo y me da un error...

esto lo hago con 2 botones, uno para crear el objeto y otro para liberarlo.
En la ayuda encontré q antes de liberar el objeto había q parar la música si estaba sonando, cosa q hice y sigue fallando.

Supongo q no conoceis el componente, pero igual teneis esperiencia con música, yo por lo q veo, no le doy tiempo a q se cierre y por eso falla, ya q si pongo en un botón, parar la música y en otro liberarlo y no los pulso muy rápido, pos no salen problemas...

He pensado en poner un sleep, pero no me gusta, aunq igual es la única solución q me queda...aunq igual hay algún método q te diga si el objeto está lista para ser liberado...


Pos nada, gracias por las contestaciones y por las molestias...

Lepe 06-10-2005 18:42:38

adlfv: FLista ¿de qué tipo es? Tiene toda la pinta de Tobjectlist o similar, y se puede destruir de otra forma más simple.

Obviamente los errores vienen aqui:
|01654424|CoreClassHab.bpl|UClassObjects.pas | |CoreClassHabDestroy |29 y 30
|01655442|CoreClassHab.bpl|UCHabitaciones.pas|THabitaciones |Destroy |429 |
|01654CD6|CoreClassHab.bpl|UCHabitaciones.pas|TBaseHabitacion|Destroy |195 |
|01658F68|CoreClassHab.bpl|UCGUIHab.pas |TGUIIcon |Destroy |127 |

En cuanto a la filosofia, lo veo muy complejo, "este ahora si lo tengo que liberar", "este otro no"... unifica criterios y objetos, los que tengan que liberarse a mano en un sitio y los demás a otro sitio.

Y siempre usa Freeandnil, ya que unos hacen referencias a otros.

lento manu:
Para el tema del número de linea revisa este hilo reciente

Saludos

Lepe 06-10-2005 18:49:18

¡¡ME VA A DAR UN INFARTO CEREBRAL !! :D

Ya he perdido la cuenta de cuantos temas se llevan en este hilo al mismo
tiempo. ¡¡¡ Por mi salud !!!, abrid un hilo distinto para
cada nueva pregunta que vaya saliendo :rolleyes:


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

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