Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Varios (https://www.clubdelphi.com/foros/forumdisplay.php?f=11)
-   -   Destruir objetos (https://www.clubdelphi.com/foros/showthread.php?t=40933)

david.rguez 02-03-2007 13:41:07

Destruir objetos
 
Buenos días:

En una aplicación que estamos desarrollando, creamos una serie de objetos TImage dinámicamente, ya que no sabemos de antemano cuántas deben crearse.
Lo hacemos mediante el siguiente código, una vez hemos encontrado en la base de datos las imágenes necesarias:

Código PHP:

while (datages.tadeposito.Eof false) do
    
begin
        imagen
:= Trim(datages.tadepositoBODEPO.Value) + 'deposito' Trim(datages.tadepositoCLADEPO.Value) + '.bmp';

        
imag:= TImage.Create(Self);
        
imag.Parent:= Self;
        
imag.Name:= 'deposito' Trim(datages.tadepositoCLADEPO.Value);
        
imag.Top:= datages.tadepositoYDEPO.Value;
        
imag.Left:= datages.tadepositoXDEPO.Value;
        
imag.Picture.LoadFromFile(ExtractFilePath(Application.ExeName) + 'imagenes/depositos/' imagen);

        
datages.tadeposito.Next;
    
end


El problema está en que el usuario puede repetir la operación, con lo que aparecen nombres duplicados, con su correspondiente error. ¿Cómo puedo hacer para poder hacer referencia a dichas imágenes creadas para destruirlas antes de realizar la nueva consulta?

Gracias

David Rguez.

Neftali [Germán.Estévez] 02-03-2007 14:07:20

Puedes utilizar un TList o un TStringList (a mi me gusta más este, ordenado y como cadena el nombre del componente); Cuando necesites encontrar uno los buscas y eliminas la imagen y el elemento de TStringList.

Código Delphi [-]
var
  ListaImagenes:TStringList;
  b:Boolean;
  Index:Integer;
begin
  // Para Crearla ordenaday sin duplicados
  ListaImagenes := TStringList.Create();
  ListaImagenes.Sorted := True;
  ListaImagenes.Duplicates := dupError;
...
  // Para añadir
  ListaImagenes.AddObject(imag.Name, Imag);
  // Para buscar
  b := ListaImagenes(imag.Name, Index);
  // En b tienes TRUE o FALSE (si la ha enconrado o no).
  // Index es el índice del elemento.

  // Para eliminarla (hay que hacer un Find antes)
  TImage(ListaImagenes.Objects[Index]).Free;
  ListaImagenes.Delete(Index);

Te he puesto el código "al vuelo" sin compilar ni nada, así que talvez exista algun error de sintaxis.

seoane 02-03-2007 14:08:02

Pensaste en la posibilidad de utilizar un TObjectList.

Crandel 02-03-2007 14:12:26

Podrias crear una lista de los componentes que vas creando y los destruis o los reutilizas en la nueva consulta.


Algo asi:

Declaras la lista, en el private de tu form

Código Delphi [-]
   Images: TList;

lo creas en el FormCreate
Código Delphi [-]
   Images := TList.Create;

en FormDestroy lo destruis
Código Delphi [-]
  for i:= 1 to Images.Count do
  begin
    Image := Images.Items[i-1];
    Image.Free;
  end;
  Images.Free;

y lo usas asi:
Código Delphi [-]
var
  i: integer;
  Image: TImage;
  NombreImg: string;
begin
  i:= 0;
  while not Datages.tadeposito.Eof do
  begin
    inc(i);
    if i > Images.Count then
      Image := TImage.Create
    else
      Image := List.Items[i-1];

      NombreImg:= Trim(datages.tadepositoBODEPO.Value) + 'deposito' + Trim(datages.tadepositoCLADEPO.Value) + '.bmp';

      imag.Parent:= Self;
      imag.Name:= 'deposito' + Trim(datages.tadepositoCLADEPO.Value);
      imag.Top:= datages.tadepositoYDEPO.Value;
      imag.Left:= datages.tadepositoXDEPO.Value;
      imag.Picture.LoadFromFile(ExtractFilePath(Application.ExeName) + 'imagenes/depositos/' + NombreImg);

      datages.tadeposito.Next;
  end; 
end;

No he probado el codigo, asi que puede contener errores.

david.rguez 02-03-2007 14:12:38

Se me había ocurrido utilizar un array de TImage para mantener una referencia a todas las imágenes. Pero... ¿cómo hago para destruir el objeto completo? No me permite acceder si no especifico un elemento del array.

seoane 02-03-2007 14:17:38

Cita:

Empezado por david.rguez
Se me había ocurrido utilizar un array de TImage para mantener una referencia a todas las imágenes. Pero... ¿cómo hago para destruir el objeto completo? No me permite acceder si no especifico un elemento del array.

No es por ser pesado, pero, pensaste en la posibilidad de utilizar un TObjectList.

david.rguez 02-03-2007 14:22:55



Crandel: He probado tu idea y ha funcionado perfectamente. Gracias :)

Crandel 02-03-2007 14:25:45

Cita:

Empezado por seoane
Pensaste en la posibilidad de utilizar un TObjectList.

muy interesante clase, no la conocia

Crandel 02-03-2007 14:27:10

Cita:

Empezado por david.rguez

Crandel: He probado tu idea y ha funcionado perfectamente. Gracias :)

puedes mejorara el codigo usando TObjectList :p, se simplifica un poco y te puede traer algunas ventajas posteriores.

seoane 02-03-2007 14:35:46

Cita:

Empezado por Crandel
puedes mejorara el codigo usando TObjectList :p, se simplifica un poco y te puede traer algunas ventajas posteriores.

Aja!!! :p :D

Neftali [Germán.Estévez] 02-03-2007 14:52:39

Cita:

Empezado por seoane
Pensaste en la posibilidad de utilizar un TObjectList.

Basicamente es lo mismo, simplemente que si hay posibilidad de ordenar la lista (utilizando un string), prefiero usar TStringList con ordenación para agilizar las búsquedas.
En este caso parece que la propiedad Name del componente es ideal para eso.
No lo he mirado, pero supongo que la búsqueda en lo TObjectList debe ser secuencial.

Neftali [Germán.Estévez] 02-03-2007 14:56:09

Me he perdido un rato de conversación.... :D:D:D
Yo también insisto (pesado que es uno...).
Si se van a destruir todos los objetos a la vez, da igual utilizar TObjectList que TStringList, pero si se van buscando de uno en uno para volver a crearlos, crea que es más eficiente TStringList.

NOTA: También hay que ver de cuantos objetos estamos hablando. Si son 5 (por decir algo) es prácticamente lo mismo. Incluso las inserciones serán más lentas en TStringList, pero si en un número un poco grande se gana mucho en los accesos.

david.rguez 02-03-2007 14:56:22

¿Qué ventajas tiene? En este caso en concreto solo necesitamos la lista para mantener una referencia para luego poder "cargarnos" las imágenes y evitar así que se dupliquen en una operación posterior.

seoane 02-03-2007 15:02:46

Cita:

Empezado por Neftali
No lo he mirado, pero supongo que la búsqueda en lo TObjectList debe ser secuencial.

Efectivamente, la única forma de localizar un objeto dentro de un TObjectList es recorrer la lista. Pero si te fijas, el no menciona como piensa acceder a los objetos, puede que incluso lo haga de forma secuencial. En lo que si hace hincapié es que le gustaría poder liberar todos los objetos a la vez, y ahí si que el TObjectList es perfecto.

Cuando añadimos un objeto a un TObjectList este se convierte en el dueño del objeto, esto implica, entre otras cosas, que cuando haces Free o FreeAndNil sobre el TObjectList automáticamente se liberan todos los objetos de la lista.

En conclusión, si se necesita hacer búsquedas por nombre, el TStringList es lo mejor, si por el contrario lo que nos interesa es que liberar los objetos sea un proceso transparente para nosotros entonces un TObjectList es una buena opción.

jhonny 02-03-2007 15:11:56

Muy buen apunte seoane pero para localizar un Objeto en el TObjectList podríamos usar el
Código Delphi [-]
function IndexOf(AObject: TObject): Integer;
que trae el TObjectList y en ese caso ¿no seria igual de efectivo buscar un objecto en un TObjectList que en un TStringList?

seoane 02-03-2007 15:16:07

Cita:

Empezado por jhonny
¿no seria igual de efectivo buscar un objecto en un TObjectList que en un TStringList?

Son cosas diferentes, en el caso del TStringList lo localizamos por el nombre (o por cualquier otra cadena de texto que le asignemos), mientras que con el método que tu indicas no se puede.

jhonny 02-03-2007 15:29:42

Se que como parámetro hay que pasarle un Objeto y al de el TStringList un string pero lo que trato de preguntar es ¿Que tan rápido(Eficiente) es buscar la posición de un objeto con este método? ¿Es igual de rápido que buscar el nombre de un objeto en un TStringList?

Pregunto esto porque si la respuesta es positiva, me parecería que en ultimas el TStrinList no terminaria siendo la mejor opción para ninguno de los dos casos.

Neftali [Germán.Estévez] 02-03-2007 15:33:17

Cita:

Empezado por david.rguez
¿Qué ventajas tiene? En este caso en concreto solo necesitamos la lista para mantener una referencia para luego poder "cargarnos" las imágenes y evitar así que se dupliquen en una operación posterior.

Como he dicho arriba, si vas a recorrer la lista secuencialmente para borrarlas todas juntas, mejor TObjectsList. TStringList tiene ventajas si vas a hacer búsquedas individuales.
EDITO: Veo que es lo mismo que te ha dicho Seoane.

seoane 02-03-2007 15:34:54

Pongamos un ejemplo jhonny. Si yo tengo una lista de objetos, y quiero acceder a un objeto sabiendo solo su nombre, no podría usando el método que tu indicas.

Crandel 02-03-2007 15:35:39

Cita:

Empezado por david.rguez
¿Qué ventajas tiene? En este caso en concreto solo necesitamos la lista para mantener una referencia para luego poder "cargarnos" las imágenes y evitar así que se dupliquen en una operación posterior.

Voy a intentar ordenarte un poco las cosas.

Si lo que necesitas es lo que planteaste al principio, con el codigo que te di es suficiente.

Ventajas de TStringGrid, en el caso tuyo es mas facil buscar un componente segun el nombre de la imagen que posee.

Ventaja del TObjectList, por ejemplo queres que al hacer doble click sobre la imagen este elimine o que se agrande en otra ventana. Tenes funciones mas rapidas como Remove(AObject: TObject) y IndexOf(AObject: TObject) que vienen de 10 en estos casos.

Si no te interesa tanto esto. Dejalo como esta.

pd: No quiere decir que con el Tlist no se pueda hacer busqueda, tambien se puede, es la clase base de las otras clases, por lo que las otras agregan algunas funcionalidades extras.

Cita:

Empezado por seoane
Aja!!!

para que veas que no soy cerrado :p, creo que todos los codigos se pueden mejorar un poco :D

Crandel 02-03-2007 15:37:17

che locos, dejen de hablar todos al mismo tiempo que no se entiende nada. A ver si sacan turno para escribir :D :D :D

Neftali [Germán.Estévez] 02-03-2007 15:45:15

Cita:

Empezado por jhonny
Se que como parámetro hay que pasarle un Objeto y al de el TStringList un string pero lo que trato de preguntar es ¿Que tan rápido(Eficiente) es buscar la posición de un objeto con este método? ¿Es igual de rápido que buscar el nombre de un objeto en un TStringList?

La diferencias es el método de Acceso.
NOTA: Habñlamos siempre de TStringList que podamos Ordenar y sin duplicados (opara poder usar el método Find con búsqueda binaria).

En el TObjectList cuando buscas con (IndexOf), se hace un recorrido desde el primer objeto mirando si es el que buscas. Si no ese ese, incrementa el índice y vuelve a comparar; Así hasta encontrarlo o llegar al final de la lista.

En TStringList (ordenado y sin duplicados) al hacer un Find se coge el elemento central de la lista y se compara. Si es igual, se compara si es mayor o menor y se busca en la mitad de la lista correspondiente empezando por el elemento central y así sucesivamente.

En media en un TObjectList harás un número de comparaciones para buscar un elemento igual a la mitad de los elementos;
En una lista de 10 harás 5 comparacoiones, en una de 100 harás 50...
10 -> 5
100 -> 50
1000 ->500
10000 - 5000
...

En media en una búsqueda binaria (la que hace TStringList) debes aplicar logaritmo.
10 -> 4
100 -> 7
1000 -> 10
10000 -> 13
....

Si te fijas la proporción se aleja cuantos más elementos hay.

jhonny 02-03-2007 15:49:02

Cita:

Empezado por seoane
Pongamos un ejemplo jhonny. Si yo tengo una lista de objetos, y quiero acceder a un objeto sabiendo solo su nombre, no podría usando el método que tu indicas.


mmm, si tienes razón en fin todo depende de para que se necesite, quizá mi cerebro se niega a creer que no venga de fabrica un método en esta clase para buscar el objeto por su nombre.

jhonny 02-03-2007 15:56:41

Muchas gracias por la extensa explicación Neftali, todo esta mas claro que el agua. ;)

roman 02-03-2007 17:52:09

En mi experiencia, a pesar del entusiasmo inicial que alguna vez tuve por el ObjectList, en general es una clase con muy pocas ventajas sobre el StringList, ya que la más de las veces requerimos referenciar a los objetos que contiene por algún identificador. La ventaja de un ObjectList, que sería la de su manejo automático de la destrucción de los ojetos, es realmente muy nimia. Una simple rutinita:

Código Delphi [-]
procedure FreeList(var List: TStringList);
var
  I: Integer;

begin
  for I := 0 to List.Count -1 do
    List[i].Free;

  FreeAndNil(List);
end;

Vamos, qué es más cómodo con ObjectList, lo es. Pero muy poco más cómodo, y la enrome ventaja de acceder a los elementos por medio de un identificador sobrepasa con creces lo otro.

// Saludos

Lepe 02-03-2007 19:35:46

Quizás estemos usando el TObjectList cuando no deberíamos usarlo.

Me explico: TObjectList está pensado para guardar una lista de objetos, su principal cualidad (por decirlo así) es encontrar los objetos por su posición (índice) o bien por su dirección (puntero de TObjects), si no necesitamos estas características, quizás no deberíamos usar esta clase.

Tenemos una lista de Edits creados en tiempo de ejecución y cuando el usuario teclea debemos localizarlo, ¿para qué complicarnos la vida añadiendo un nombre único a cada TEdit y después usar ese nombre para encontrarlo? Si tenemos el objeto Sender ya podemos encontrar por ese puntero (haciendo uso de TObjectList.IndexOf) el Tedit en cuestión dentro de nuestra lista (no creo que vayamos a tener 10.000 TEdits creados en un Form ;)). Usar FindComponent será más costoso que el TobjectList.IndexOf, precisamente porque habrá más controles creados en el form, y su búsqueda tardará más.

Otra pega que le veo al StringList, es precisamente tener un único identificador para localizar el objeto, ¿qué ocurre si quiero localizar un Objeto por alguna propiedad que lleva dentro dicho objeto?, pues tenemos la misma desventaja que al usar TObjectList, tenemos que hacer un bucle, recorriendo todos los objetos que tiene el StringList y buscando la dichosa propiedad.

Yo al menos, jamás he derivado de TStrings o TStringList para obtener funcionalidades extras, sin embargo, si lo he hecho de TObjectList para tratar mis propios objetos sin tener que aplicar moldeo de tipos en mi programa principal. Para mí, ya es muy clarificador el código resultante y si le veo utilidad hacerlo.

El método Sort de un TObjectList es también muy útil, ya que creando varias funciones de ordenamiento permite realizar auténticas virguerías con los objetos creados.

Aquí un ejemplo práctico:
TFolders es un TObjectList, cada elemento vuelve a ser un TObjectList (TFolder) , por último, cada elemento de este último TObjectList es un TObject a secas (TPathElement), que tendrá una serie de propiedades inherentes a cada archivo, como:
- ruta relativa de una dada
- ruta absoluta
- tamaño del archivo
- etc.

Código Delphi [-]
{------------------------------------------------------------------------------
  TFolders son las carpetas  que se van a crear

  Es una lista que contiene una lista dual

  TFolders
     |--------- TFolder
     |             |------- Archivos encontrados (TPathElement)
     |             |------- Archivos encontrados (TPathElement)
     |             |------- Archivos encontrados (TPathElement)
     |
     |--------- TFolder
                   |------- Archivos encontrados (TPathElement)
                   |------- Archivos encontrados (TPathElement)

-------------------------------------------------------------------------------}

// posibilidades de ordenación:
{ Por el nombre de archivo (scaption)
   por el tamaño de carpeta (sValue)
   sin ordenación (sNone)
}
  TSort = (sCaption, sValue,sNone);
   
  TFolder = class(TDualList)
    public
     function Size :Int64; // suma el tamaño de todos los archivos que contiene
  end;

  TFolders = class(TObjectList)
    private
      function GetItem(Index: Integer): TFolder;
      procedure SetItem(Index: Integer; const Value: TFolder);
    public
      function Add(AObject: TFolder): Integer;
      procedure Sort(const SortBy:TSort;const Order:TSortOrder);overload;
      procedure Sort(funcion:TListSortCompare);overload;

      property Items[Index: Integer]: TFolder read GetItem write SetItem; default;
  end;

Saludos

roman 02-03-2007 20:42:47

Cita:

Empezado por Lepe
Quizás estemos usando el TObjectList cuando no deberíamos usarlo.

Llevas razón y son interesantes los ejemplos. No obstante, me gustaría apuntar algunas cosas:

1. TStringList tiene el método IndexOfObject que funciona igual que el de TObjectList (TList). Hace un ciclo en busca del apuntador, y con ello encuentras fácilmente un objeto.

2. TStringList tiene el método CustomSort, que funciona igual que el Sort de TList: se le pasa como parámetro una función de comparación. En el caso de TObjectList, la función recibe dos apuntadores (básicamente los objetos) y en el caso de TStringList recibe dos índices con los cuales es inmediato obtener los objetos.

3. En la búsqueda, estamos igual, como ya observaste.

El ejemplo que pones, es muy interesante, pero hay que notar que ahí ya estamos hablando, no de un lista realmente, sino de una estructura ramificada. Pero aún así, usando TStringList también podríamos hacerlo, con la ventaja extra, por ejemplo, de poder colocar el nombre del archivo y así agilizar las búsquedas por nombre.

// Saludos

mamcx 02-03-2007 22:48:50

Creo que en tal caso una lista hash seria un mejor candidato. Aunque de forma lastimosa Delphi no viene de fabrica con una bien pensada y la que ahi esta "escondidad" y como que no es muy eficiente...

Lepe 03-03-2007 03:31:59

No voy a hacer contrarreplica de todo, las ideas están mostradas y en definitiva eso es lo que importa.

Cita:

Empezado por roman
Pero aún así, usando TStringList también podríamos hacerlo, con la ventaja extra, por ejemplo, de poder colocar el nombre del archivo y así agilizar las búsquedas por nombre.

Si te fijas el objeto final se llama TPathElement, no se llama TFoundFile, TFile o similar, esto es porque realmente lo que importaba en este caso son las distintas rutas de cada archivo:
- ruta original
- ruta relativa a partir de una profundidad específica
- ruta de destino inicial
- Nombre del archivo a secas para mostrar al usuario.
- ruta final donde se copiará el archivo (devolverá la concatenación de: ruta de destino inicial + ruta relativa+ Nombre de archivo)

El tener alguna de esas propiedades fuera del objeto me hace trabajar más para mantener sincronizada la informacion, sobre todo dando la posibilidad al usuario de cambiar un archivo de carpeta mediante drag&drop que implicaría actualizar ese String fuera del objeto.

Saludos


La franja horaria es GMT +2. Ahora son las 01:35:35.

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