Ir al contenido


Foto

¿Cómo liberar un objeto en un TCombobox?


  • Por favor identifícate para responder
45 respuestas en este tema

#1 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 12 marzo 2017 - 08:04

Pues eso, ¿cual sería la forma correcta de liberar los objectos agregados  a un item de un combobox?, he probado varias formas y todas marcan error al intentar liberarlas, lo que intento hacer es resetear el combobox, dejo los siguientes códigos:
 
Aquí agrego los items:


delphi
  1. Type
  2. TPlayer = class(TObject)
  3. PId: Integer;
  4. PRating: Integer;
  5. end;
  6.  
  7. Player := TPlayer.create;
  8. Player.PId := 1;
  9. Player.PRating := 1400;
  10.  
  11. ComboBox1.Items.AddObject('Fernando',Player);

Aquí trato de limpiar el combobox:


delphi
  1. TPlayer(ComboBox1.Items.Objects[0]).Free;
  2. ComboBox1.Items.Delete(0);

También intenté simplemente:


delphi
  1. ComboBox1.Items.Objects[0].Free;
  2. ComboBox1.Items.Delete(0);

Por ültimo:


delphi
  1. ComboBox1.Items.Clear;

Todos me Marcan error.
 
Los códigos anteriores es un ejemplo con un sólo item, los combos tienen más de 20.
 
Saludos.


  • 0

#2 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 12 marzo 2017 - 08:34

El error marca en la siguiente linea:

 

function TCustomListBox.TListBoxStrings.GetObject(Index: Integer): TObject;
begin
  Result := FListBox.ListItems[Index].Data;
end;

 

 

Tengo un ListBox en el mismo formulario, tengo un tabcontrol con dos tabs en una está el ListBox y el otro donde están los combos, si los combos no los rellenos no hay problema, pero si los rellenos y paso al tab donde esta el listbox y en el proceso se limpia los combos pues bang!. no sé porqué afecta el listbox.


  • 0

#3 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 12 marzo 2017 - 09:52

Esto es en Firemonkey o en Vcl?


  • 0

#4 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 12 marzo 2017 - 10:12

Voy a ir investigando paso a paso

 

La primer prueba mas basica que hice, y no me dio error fue esta:


delphi
  1. procedure TForm2.FormCreate(Sender: TObject);
  2. begin
  3. ReportMemoryLeaksOnShutdown := True;
  4. end;
  5.  
  6. procedure TForm2.btnAddClick(Sender: TObject);
  7. var
  8. Player: TPlayer;
  9. begin
  10. Player := TPlayer.create;
  11. Player.PId := 1;
  12. Player.PRating := 1400;
  13. ComboBox1.Items.AddObject('Fernando', Player);
  14. end;
  15.  
  16. procedure TForm2.btnClearClick(Sender: TObject);
  17. begin
  18. ComboBox1.Items.Clear;
  19. end;

Pero esto tiene fugas de memoria, ya que los objetos TPlayer no son liberados

 

Lo correcto seria, quien crea al objeto TPlayer tiene la responsabilidad de liberarlo, esto lo podes almacenar en alguna lista o arreglo y liberarlos en otro momento

 

Otra solucion si queres que el ComboBox haga todo es con un ayudante. Yo no te recomiendo esto, no permitas nunca que un control visual se vuelva tu estructura de datos, pero ahi va:


delphi
  1. type
  2. TStringsHelper = class helper for TStrings
  3. public
  4. procedure ClearAll;
  5. end;
  6.  
  7. implementation
  8.  
  9. procedure TStringsHelper.ClearAll;
  10. var
  11. I: Integer;
  12. Objs: TArray<TObject>;
  13. begin
  14. // obtener todos los objetos almacenados en el TString
  15. Objs := ToObjectArray;
  16. // invocar a Free en cada uno
  17. for I := System.High(Objs) downto System.Low(Objs) do
  18. Objs[I].Free;
  19.  
  20. // invocar al metodo Clear de la instancia TStrings
  21. Clear;
  22. end;

El orden de los metodos es importante: si primero invocas a Clear el llamado a ToObjectArray devuelve un arreglo vacio


  • 0

#5 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 12 marzo 2017 - 10:14

Tengo un ListBox en el mismo formulario, tengo un tabcontrol con dos tabs en una está el ListBox y el otro donde están los combos, si los combos no los rellenos no hay problema, pero si los rellenos y paso al tab donde esta el listbox y en el proceso se limpia los combos pues bang!. no sé porqué afecta el listbox.

 

No creo que el ListBox o el TabControl tengan la culpa, los he agregado y no pasa nada. El TabControl en realidad queda fuera de la ecuacion.

 

Si me interesa que haces con el ListBox y con todos los combos: comparten referencias al mismo TPlayer? comparten referencias al mismo objeto TStrings? Estas copiando los TStrings entre los distintos controles? Como los copias? 

 

A mi me parece que estas liberando de memoria mas de una vez el mismo TPlayer


  • 0

#6 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 12 marzo 2017 - 10:28

Pues el lisbox no tiene ninguna referencia a TPlayer, o sea, no tiene relación aguna con los combobox, ahora, en realidad son dos combos que hacen referencia a TPlayer.


  • 0

#7 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 12 marzo 2017 - 11:10

Pues eso, ¿cual sería la forma correcta de liberar los objectos agregados  a un item de un combobox?, he probado varias formas y todas marcan error al intentar liberarlas, lo que intento hacer es resetear el combobox, dejo los siguientes códigos:
 
Aquí agrego los items:


delphi
  1. Type
  2. TPlayer = class(TObject)
  3. PId: Integer;
  4. PRating: Integer;
  5. end;
  6.  
  7. Player := TPlayer.create;
  8. Player.PId := 1;
  9. Player.PRating := 1400;
  10.  
  11. ComboBox1.Items.AddObject('Fernando',Player);

Aquí trato de limpiar el combobox:


delphi
  1. TPlayer(ComboBox1.Items.Objects[0]).Free;
  2. ComboBox1.Items.Delete(0);

También intenté simplemente:


delphi
  1. ComboBox1.Items.Objects[0].Free;
  2. ComboBox1.Items.Delete(0);

Por ültimo:


delphi
  1. ComboBox1.Items.Clear;

Todos me Marcan error.
 
Los códigos anteriores es un ejemplo con un sólo item, los combos tienen más de 20.
 
Saludos.

 

No se como lo estas haciendo para más de un elemento, pero si estás eliminando múltiples elementos en un bucle ten en cuenta que por cada uno que elimines la cuenta de objetos disminuye con lo que el buble no puede ser desde 0 hasta Items.Count, necesita otro diseño:

 

Siguiendo el código que expones pero en un bucle:


delphi
  1. // Creando elementos
  2. procedure TForm1.Button1Click(Sender: TObject);
  3. var
  4. Player: TPlayer;
  5. i: integer;
  6. begin
  7. for i:= 0 to 10 do
  8. begin
  9. Player := TPlayer.create;
  10. Player.PId := 1;
  11. Player.PRating := 1400;
  12. ComboBox1.Items.AddObject(IntToStr(i), Player);
  13. end;
  14. end;
  15.  
  16. // Destruyendo elementos
  17. procedure TForm1.Button2Click(Sender: TObject);
  18. begin
  19. while ComboBox1.Items.Count > 0 do
  20. begin
  21. ComboBox1.Items.Objects[0].Free;
  22. ComboBox1.Items.Delete(0);
  23. end;
  24. end;

No se se es tu caso...

 

 

Otra forma sería esta:


delphi
  1. procedure TForm1.Button3Click(Sender: TObject);
  2. var
  3. i: integer;
  4. begin
  5. for i:= 0 to ComboBox1.Items.Count -1 do
  6. ComboBox1.Items.Objects[i].Free;
  7. ComboBox1.Items.Clear;
  8. end;

Saludos.


  • 0

#8 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 12 marzo 2017 - 11:14

Sí es en un Bucle, dejame intentarlo como comentas.


  • 0

#9 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 12 marzo 2017 - 11:18

MI caso es en el segundo código que comentas, el primero no sabría cómo pasarle el índice en el while.


  • 0

#10 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 12 marzo 2017 - 11:24

MI caso es en el segundo código que comentas, el primero no sabría cómo pasarle el índice en el while.

 

En el primer caso eliminas todos los elementos, empezando por el [0]. El tema es que al eliminarlo, el siguiente elemento pasa a ser el [0] y así hasta que no quede ninguno, por lo tanto siempre estás eliminando el elemento de indice [0].

 

En el segundo caso, eliminas los objetos asociados para luego limpiar todas las cadenas del StringList de golpe.

 

En el hipotético caso de que no todos los elementos tengan asociado un objeto, tendrás que comprobar  esta situación antes de eliminarlo, pues saltará un error al eliminar un objeto inexistente...

 

 

Saludos.


  • 0

#11 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 12 marzo 2017 - 11:32

Probé el primer caso, obtengo un error:

 

---------------------------
Debugger Exception Notification
---------------------------
Project ligacde.exe raised exception class EInvalidPointer with message 'Invalid pointer operation'.
---------------------------
Break   Continue   Help   
---------------------------

 

 

Y me marca en esta línea:


delphi
  1. procedure TObject.FreeInstance;
  2. begin
  3. CleanupInstance;
  4. _FreeMem(Pointer(Self));
  5. end;

De la unidad System.

 

Cuando viene a ver desisto de usar objetos y utlizos StringLists para almacenar la información.


  • 0

#12 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 12 marzo 2017 - 11:40

El metodo que deje arriba limpia la lista correctamente, debes usa un for inverso, o un for downto


delphi
  1. type
  2. TStringsHelper = class helper for TStrings
  3. public
  4. procedure ClearAll;
  5. end;
  6.  
  7. implementation
  8.  
  9. procedure TStringsHelper.ClearAll;
  10. var
  11. I: Integer;
  12. Objs: TArray<TObject>;
  13. begin
  14. // obtener todos los objetos almacenados en el TString
  15. Objs := ToObjectArray;
  16. // invocar a Free en cada uno
  17. for I := System.High(Objs) downto System.Low(Objs) do
  18. Objs[I].Free;
  19.  
  20. // invocar al metodo Clear de la instancia TStrings
  21. Clear;
  22. end;


  • 0

#13 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 12 marzo 2017 - 11:42

Probé el primer caso, obtengo un error:

 

 

Y me marca en esta línea:


delphi
  1. procedure TObject.FreeInstance;
  2. begin
  3. CleanupInstance;
  4. _FreeMem(Pointer(Self));
  5. end;

De la unidad System.

 

Cuando viene a ver desisto de usar objetos y utlizos StringLists para almacenar la información.

 

 

Algo más hay en tu código o tratas de destruir objetos inexistentes. El código que expuse es un ejemplo probado en proyecto limpio expresamente y en delphi7.

 

Mira a ver si como decía Agustin Ortu, destruyes dos veces los objetos... También estoy de acuerdo con él en lo referente de no usar un objeto visual como estructura de mis datos, aunque este no tiene por que ser el caso. De todas formas, la destrucción debe funcionar correctamente.

 

 

Saludos.


  • 1

#14 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 12 marzo 2017 - 11:44

Es que seguramente insisto, estas liberando la misma instancia mas de una vez, ya que me comentaste que los ComboBox comparten los TStrings

 

Prueba a escribir un destructor para TPlayer:


delphi
  1. type
  2. TPlayer = class
  3. ...
  4. public
  5. destructor Destroy; override;
  6. end;
  7.  
  8. destructor TPlayer.Destroy;
  9. begin
  10. ShowMessage('destruyendo TPlayer');
  11. inherited Destroy;
  12. end;

Y yo tambien pondria un punto de ruptura en este destructor


  • 0

#15 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 12 marzo 2017 - 11:54

Es que seguramente insisto, estas liberando la misma instancia mas de una vez, ya que me comentaste que los ComboBox comparten los TStrings


Desconocía ese punto. Entonces enecumene tendrá que repasar el diseño, u colocar un puntero nulo en cada elemento destruido y comprobar ese estado antes de intentar una nueva destrucción:


delphi
  1. if ComboBox1.Items.Objects[0] <> nil then
  2. begin
  3. ComboBox1.Items.Objects[0].Free;
  4. ComboBox1.Items.Objects[0]:= nil;
  5. end;

Saludos.


  • 0

#16 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 12 marzo 2017 - 11:56

Agustín, probé tu Helper, y me da el mismo error que el de escafandra:

 

---------------------------
Debugger Exception Notification
---------------------------
Project ligacde.exe raised exception class EInvalidPointer with message 'Invalid pointer operation'.
---------------------------
Break   Continue   Help   
---------------------------
 

Eliminé todo donde haya duplicación y aún así, dejenme probar con un sólo Combo y les comento.


  • 0

#17 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 12 marzo 2017 - 11:58

Pues me marca el error del primer caso, desisto, usaré Listas 8o|


  • 0

#18 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 12 marzo 2017 - 12:06

No te desesperes :)

 

Pon mas codigo a ver si damos con el problema. Como agregas elementos a ambos combos, como los destruyes, que haces con ello, pon todo lo relevante 


  • 0

#19 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 12 marzo 2017 - 12:07

Desconocía ese punto. Entonces enecumene tendrá que repasar el diseño, u colocar un puntero nulo en cada elemento destruido y comprobar ese estado antes de intentar una nueva destrucción:


delphi
  1. if ComboBox1.Items.Objects[0] <> nil then
  2. begin
  3. ComboBox1.Items.Objects[0].Free;
  4. ComboBox1.Items.Objects[0]:= nil;
  5. end;

Saludos.

 

Igual, me marca en la línea:


delphi
  1. function TCustomListBox.TListBoxStrings.GetObject(Index: Integer): TObject;
  2. begin
  3. Result := FListBox.ListItems[Index].Data;
  4. end;


  • 0

#20 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 12 marzo 2017 - 12:11

 

Igual, me marca en la línea:


delphi
  1. function TCustomListBox.TListBoxStrings.GetObject(Index: Integer): TObject;
  2. begin
  3. Result := FListBox.ListItems[Index].Data;
  4. end;

El error parece debido a que el Item de ese índice no existe, Creo que debes poner todo el código que usas para destruir, el bucle completo.

 

 

Prueba en un proyecto limpio con un sólo combo.

 

 

 

Saludos.


  • 0




IP.Board spam blocked by CleanTalk.