Ir al contenido


Foto

¿Cómo liberar un objeto en un TCombobox?


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

#21 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 12 marzo 2017 - 12:38

Este es el código para cargar los combos:


delphi
  1. procedure TFMain.GetSelectPlayersByModal(AModal: Integer);
  2. var QPlayers: TFDQuery;
  3. Players: TPlayer;
  4. begin
  5.  
  6. AniIndP1.Visible := True;
  7. AniIndP2.Visible := True;
  8.  
  9. AniIndP1.Enabled := True;
  10. AniIndP2.Enabled := True;
  11.  
  12. GetAllPlayersRates;
  13.  
  14. QPlayers := TFDQuery.Create(nil);
  15. QPlayers.Connection := Datos.dbDatos;
  16. QPlayers.SQL.Text := 'select * from jugadores_rating where modal_id = ' + IntToStr(AModal) + ' order by player_alias';
  17. try
  18. QPlayers.Open;
  19.  
  20. if not QPlayers.IsEmpty then begin
  21. QPlayers.First;
  22. while not QPlayers.Eof do begin
  23. Players := TPlayer.Create;
  24. Players.PId := QPlayers.FieldByName('player_id').AsInteger;
  25. Players.PRating := QPlayers.FieldByName('rating').AsInteger;
  26.  
  27. //Los Combos
  28. edWinner.Items.AddObject(QPlayers.FieldByName('player_alias').AsString,Players);
  29. edLoser.Items.AddObject(QPlayers.FieldByName('player_alias').AsString,Players);
  30.  
  31. QPlayers.Next;
  32. end;
  33.  
  34. edWinner.Enabled := True;
  35. edLoser.Enabled := True;
  36. end else begin
  37. edWinner.Enabled := False;
  38. edLoser.Enabled := False;
  39. end;
  40.  
  41. finally
  42. QPlayers.Free;
  43.  
  44. AniIndP1.Visible := False;
  45. AniIndP2.Visible := False;
  46.  
  47. AniIndP1.Enabled := False;
  48. AniIndP2.Enabled := False;
  49. end;
  50. end;

Aquí lo llamo:


delphi
  1. procedure TFMain.edModalidadChange(Sender: TObject);
  2. begin
  3. getSelectPlayersByModal(TModalidad(edModalidad.Items.Objects[edModalidad.ItemIndex]).MId);
  4. end;

Aquí hago la limpieza:


delphi
  1. procedure TFMain.cleanNewGameForm;
  2. begin
  3. edFecha.Text := '';
  4. edRatingWinner.Text := '';
  5. edRatingLoser.Text := '';
  6. edProbWinner.Text := '';
  7. edProbLoser.Text := '';
  8. edDistancia.Text := '';
  9. edWinsWinner.Text := '';
  10. edWinsLoser.Text := '';
  11. edNewRating1.Text := '';
  12. edNewRating2.Text := '';
  13.  
  14. //Liberamos los objetos de los combos
  15. Rutinas.FreePlayersObjectsFromCombo(edWinner);
  16. Rutinas.FreePlayersObjectsFromCombo(edLoser);
  17.  
  18. //Luego los colocamos en su estado original
  19. edModalidad.ItemIndex := -1;
  20. edWinner.Enabled := False;
  21. edLoser.Enabled := False;
  22. end;

Aquí la rutina de Limpieza:


delphi
  1. procedure FreePlayersObjectsFromCombo(ACombo: TComboBox);
  2. var i: Integer;
  3. begin
  4. for i := 0 to ACombo.Items.Count -1 do begin
  5. if ACombo.Items.Objects[i] <> nil then begin
  6. ACombo.Items.Objects[i].Free;
  7. ACombo.Items.Objects[i] := nil;
  8. end;
  9. end;
  10. end;

Quería ponerles un Gif, pero me ha costado el asunto 8o|


  • 0

#22 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 12 marzo 2017 - 12:59

El asunto está claro. Liberas dos veces los objetos pero no te sirve ponerlo a nil puesto que son listas diferentes. La solución es usar una lista común o solo destruir los objetos de un Combo y las cadenas de los dos.

 

 

Saludos.


  • 1

#23 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 12 marzo 2017 - 01:07

Cómo usaría una lista para cargarlo en ambos combos y obtener su info?, te refieres a un Stringlist ó un TObjectList (éste último no estoy muy empapado).


  • 0

#24 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 12 marzo 2017 - 01:20

Cómo usaría una lista para cargarlo en ambos combos y obtener su info?, te refieres a un Stringlist ó un TObjectList (éste último no estoy muy empapado).

 

Un TList o TObjectList.

 

Pero tal como lo tienes, si solo borras los objetos de un combo ya te funcionará, la Idea es borrar primero objetos del primer combo, luego sus Cadenas. Para el otro combo solo debes borrar cadenas puesto que los objetos ya no existen. Cada combo guarda una lista de punteros a los objetos, si destruyes desde uno, en el otro la lista apuntará a objetos inexistentes y aquí aparece el error que obtienes.

 

 

Saludos.


  • 0

#25 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 12 marzo 2017 - 01:30

Pues lo acabo de hacer así y me marca el mismo error apuntanto a TCustomListBox, algo me dice que durante la limpieza de los combos afecta al listbo que tengo, y eso que no tiene referencia con TPlayer, aquí el código cómo cargo el listbox:


delphi
  1. procedure TFMain.ListFilteredGames;
  2. var Items: TListBoxItem;
  3. Games: TFDMemTable;
  4. begin
  5.  
  6. listGames.Items.Clear;
  7.  
  8. listGames.BeginUpdate;
  9.  
  10. Games := GetRequest('/partidas/filtro/25',rmGET,AniIndicator1);
  11.  
  12. if not Games.IsEmpty then
  13. begin
  14.  
  15. Games.First;
  16. while not Games.Eof do begin
  17.  
  18. Items := TListBoxItem.Create(listGames);
  19. Items.Parent := listGames;
  20. Items.Name := 'Game' + Games.FieldByName('id').AsString;
  21. Items.Height := 50;
  22. //Items.Data := TObject(Games.FieldByName('id').AsInteger);
  23. Items.StyleLookup := 'itemGames';
  24. Items.StylesData['winnerresult'] := Games.FieldByName('wins1').AsString;
  25. Items.StylesData['loserresult'] := Games.FieldByName('wins2').AsString;
  26. Items.StylesData['winneralias'] := Games.FieldByName('winner').AsString;
  27. Items.StylesData['loseralias'] := Games.FieldByName('loser').AsString;
  28. //Items.StylesData['rectanglestyle.onclick'] := TValue.From<TNotifyEvent>(GetGameInfoClick);
  29. Items.ItemData.Bitmap.LoadFromURL(baseImg + '/bolas/' + Games.FieldByName('bola2').AsString);
  30. //Items.OnClick := GetGameInfoClick;
  31.  
  32. Games.Next;
  33. end;
  34. end;
  35.  
  36. listGames.EndUpdate;
  37. end;

Se fijará que comenté algunas líneas donde intervienen objetos, aunque no sea TPlayer, por si acaso.


  • 0

#26 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 12 marzo 2017 - 01:51

Estoy seguro de que esta linea es problematica


delphi
  1. //Items.Data := TObject(Games.FieldByName('id').AsInteger);

Un Integer no es un TObject 


  • 0

#27 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 12 marzo 2017 - 01:51

Pero ese código no es el que pusiste antes llenando los ComboBox. :|

 

Yo me refiero a hacer esto:


delphi
  1. procedure FreePlayersObjectsFromCombo;
  2. var i: Integer;
  3. begin
  4. for i := 0 to edWinner.Items.Count -1 do
  5. edWinner.Items.Objects[i].Free;
  6.  
  7. edWinner.Items.Clear;
  8. edLoser.Items.Clear;
  9. end;

Saludos.


  • 0

#28 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 12 marzo 2017 - 01:54

Pero ese código no es el que pusiste antes llenando los ComboBox. :|

 

 

En realidad ahora nos damos cuenta de que son dos problemas distintos  ;)

 

Solucionar los Combo no te va a solucionar el ListBox


  • 0

#29 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 13 marzo 2017 - 04:55

Estoy seguro de que esta linea es problematica


delphi
  1. //Items.Data := TObject(Games.FieldByName('id').AsInteger);

Un Integer no es un TObject 

 

Pues no he sido el único que lo utiliza de esa manera, eso lo aprendí de un post de ejemplo de Nick Hodges hace un tiempo y luego lo vi utilizarlo Zarko, y aún lo estoy viendo en algunos foros que ofrecen esa solución, de todos modos esa la línea lo comenté pensando que esa era el problema porque la línea que marca de la clase TCustomListBox tiene relación con eso y el problema persiste.

 

 

Pero ese código no es el que pusiste antes llenando los ComboBox. :|

 

Yo me refiero a hacer esto:


delphi
  1. procedure FreePlayersObjectsFromCombo;
  2. var i: Integer;
  3. begin
  4. for i := 0 to edWinner.Items.Count -1 do
  5. edWinner.Items.Objects[i].Free;
  6.  
  7. edWinner.Items.Clear;
  8. edLoser.Items.Clear;
  9. end;

Saludos.

 

No, lo posteé pensando si mi vista fallaba y el problema radicaba por ahí, y el código que comentas la tengo tal como lo sugeriste, y el problema persiste.

 

Debo decir, que creo el problema está relacionado con el TabControl, porque fíjense, en la página donde están los formularios en un combo maestro (Por llamarlo así) tengo el siguiente código:


delphi
  1. procedure TFMain.edModalidadChange(Sender: TObject);
  2. begin
  3. getSelectPlayersByModal(TModalidad(edModalidad.Items.Objects[edModalidad.ItemIndex]).MId);
  4. end;
  5.  
  6. procedure TFMain.GetSelectPlayersByModal(AModal: Integer);
  7. var QPlayers: TFDQuery;
  8. Players: TPlayer;
  9. begin
  10.  
  11. AniIndP1.Visible := True;
  12. AniIndP2.Visible := True;
  13.  
  14. AniIndP1.Enabled := True;
  15. AniIndP2.Enabled := True;
  16.  
  17. GetAllPlayersRates;
  18.  
  19. //Aquí llamo la limpieza cada vez que cambio de valor
  20. Rutinas.FreePlayersObjectsFromCombo(edWinner,edLoser);
  21.  
  22. QPlayers := TFDQuery.Create(nil);
  23. QPlayers.Connection := Datos.dbDatos;
  24. QPlayers.SQL.Text := 'select * from jugadores_rating where modal_id = ' + IntToStr(AModal) + ' order by player_alias';
  25. try
  26. QPlayers.Open;
  27.  
  28. if not QPlayers.IsEmpty then begin
  29. QPlayers.First;
  30. while not QPlayers.Eof do begin
  31. Players := TPlayer.Create;
  32. Players.PId := QPlayers.FieldByName('player_id').AsInteger;
  33. Players.PRating := QPlayers.FieldByName('rating').AsInteger;
  34.  
  35. edWinner.Items.AddObject(QPlayers.FieldByName('player_alias').AsString,Players);
  36. //edLoser.Items.AddObject(QPlayers.FieldByName('player_alias').AsString,Players);
  37.  
  38. QPlayers.Next;
  39. end;
  40.  
  41. edWinner.Enabled := True;
  42. edLoser.Enabled := True;
  43. end else begin
  44. edWinner.Enabled := False;
  45. edLoser.Enabled := False;
  46. end;
  47.  
  48. finally
  49. QPlayers.Free;
  50.  
  51. AniIndP1.Visible := False;
  52. AniIndP2.Visible := False;
  53.  
  54. AniIndP1.Enabled := False;
  55. AniIndP2.Enabled := False;
  56. end;
  57. end;

Cuando realizo a limpieza en la misma página ella funciona perfectamente, insisto debe ser el TabControl, dejenme intentar crear un Gif, sino me costará subir el video.

 

Saludos


  • 0

#30 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 13 marzo 2017 - 05:32

Desistí del Gif, así que os pongo el vídeo:

 

https://drive.google...aEFzX0VIVHRtb0k


  • 0

#31 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 13 marzo 2017 - 06:15

Aquí está tu gif:

a23dd25e2dcff0a2095247389d38a02do.gif

 

El vídeo muestra el error en el segundo intento, pero no me hago a la idea del código que se ejecuta en el primer intento y en el segundo. ¿No estarás borrando en un evento y luego en otro distinto, o en el mismo que se repite? Debes cerciorarte de el código de borrado no se repite.

 

Otra cosa, tienes comentada la línea


delphi
  1. //edLoser.Items.AddObject(QPlayers.FieldByName('player_alias').AsString,Players);

Con lo que no se llena el combo edLoser, supongo que luego tampoco estarás borrando su contenido....

 

Vigila el evento que se dispare al hacer el cambio en el TabControl. ¿es ahí donde borras y destruyes los objetos? ¿se ejecuta otra vez cuando ya se borró? Si es el caso y para un solo combo, el asignar nil a los elementos destruidos puede ser útil para evitar destruirlos nuevamente en el mismo combo, pero ojo, si se comparten en dos combos, sólo debes destruirlos en el primero.

 

En ocasiones pueden aparecer errores de memoria en un punto de ejecución lejano al punto donde se produjo el bug y eso puede despistar bastante a la hora de depurar. Este tipo de error suele producirse por uso de punteros que se descontrolan en un índice de bucle sobrepasado, liberar un objeto inexistente, liberar memoria que no se reservó o que ya se liberó, reentradas en una función recursiva que consume la pila... las situaciones pueden ser múltiples pero el fallo más simple puede ser un error de índice a un objeto cuando se crean de forma dinámica.

 

Muchas veces estos errores no se ven a simple vista mirando el código. Tendrás que ejecutar paso a paso y comprobar el estado de las variables problemáticas, los valores de los índices y si sobrepasan el máximo permitido, la existencia real de objetos que vas a liberar (viendo el contenido coherente de su contenido en el debugger)

 

 

Saludos.


  • 0

#32 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 13 marzo 2017 - 06:26

Pues no he sido el único que lo utiliza de esa manera, eso lo aprendí de un post de ejemplo de Nick Hodges hace un tiempo y luego lo vi utilizarlo Zarko, y aún lo estoy viendo en algunos foros que ofrecen esa solución, de todos modos esa la línea lo comenté pensando que esa era el problema porque la línea que marca de la clase TCustomListBox tiene relación con eso y el problema persiste.

 

 

Ya se que "se puede", el problema es que luego estas invocando un Free sobre esa referencia. Este simple codigo arroja un AccessViolation


delphi
  1. program Project1;
  2.  
  3. {$APPTYPE CONSOLE}
  4.  
  5. {$R *.res}
  6.  
  7. uses
  8. System.SysUtils;
  9.  
  10. var
  11. Obj: TObject;
  12. begin
  13. try
  14. Obj := TObject(1234);
  15. try
  16. Obj.Free;
  17. except
  18. on E: Exception do
  19. Writeln(E.ClassName, ': ', E.Message);
  20. end;
  21. finally
  22. Readln;
  23. end;
  24. end.


  • 0

#33 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 13 marzo 2017 - 06:35

Hice la siguiente prueba, no cargo el listBox así que no hay items en ella, hago el proceso, vuelvo hacia atrás, todo bien,  regreso a los comboboxs, ésta vez no hago limpieza, bang!, vuelve el error, vuelvo otra vez al proceso, ésta vez no cargo ningún combo, o sea, no creo ningún objeto TPlayer, aquí no sucede el error, el error se presenta luego de crear los objetos, si yo no regreso hacia atrás, al primer tab, ella limpia bien los combos, creo que deberé manejarlo de otra forma, estuve viendo la Clase TObjectList, se ve interesante, pero no encuentro un ejemplo concreto para mi situación.

 

Agustín, aunque nunca me ha ocurrido error de ese tipo, al menos en VCL, ¿me recomienda mejor crear una clase TObject como lo he estado haciendo?, aunque lo primero me ahorra muchas líneas. :D


  • 0

#34 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 13 marzo 2017 - 06:39

 

Ya se que "se puede", el problema es que luego estas invocando un Free sobre esa referencia.

 

En principio la linea estaba comentada, no sabemos el código real que está ejecutándose, pero como comenta Agustin Ortu, es un ejemplo claro de una liberación de un objeto inexistente que generará un error en ese momento o más adelante, según la implementación.

 

 

Saludos.


  • 0

#35 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 13 marzo 2017 - 06:54

Ya encontré el p**o problema!! :| , pues en el procedimiento de limpieza tenía lo siguiente:


delphi
  1. //Nos aseguramos que no haya nada seleccionado
  2. edWinner.ItemIndex := -1;
  3. edLoser.ItemIndex := -1;

Eso era lo que estaba causando el bendito problemas, 2 benditos días perdidos. :D .

 

off-Topic: Me gustaría alguien hiciera una explicación del uso TObjectList<T>, incluyendo ejemplos como en mi situación, claro en un hilo aparte. *-)


  • 0

#36 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 13 marzo 2017 - 08:56

Ya encontré el p**o problema!! :| , pues en el procedimiento de limpieza tenía lo siguiente:


delphi
  1. //Nos aseguramos que no haya nada seleccionado
  2. edWinner.ItemIndex := -1;
  3. edLoser.ItemIndex := -1;

Eso era lo que estaba causando el bendito problemas, 2 benditos días perdidos. :D .

 

off-Topic: Me gustaría alguien hiciera una explicación del uso TObjectList<T>, incluyendo ejemplos como en mi situación, claro en un hilo aparte. *-)

 

No estuve muy al tanto del hilo, pero de lo que estuve leyendo y creo estar entendiendo el verdadero problema que tu tienes se debe a que no tienes estrictamente hablando una separación entre el dominio y la presentación. Tu haces que un ComboBox sea "dueño", cree, y elimine objetos TPlayer y que además ese listado sea compartido con un segundo Combo.

 

Para esos casos, me parece a mi que lo más apropiado es un enfoque mucho más OO purista del que estás empleando. Deberías tener una clase propia que se encargue de manejar la lista de TPlayer, como estoy falto de inspiración llamemosla TPlayerList. Esta clase es la que central; los combos son simplemente elementos visuales que van a mostrar el contenido. Ninguno de estos dos combos o cualquier elemento visual debería tener la responsabilidad de crear, ni liberar los objetos Players que vayas creando.

Por supuesto que es muy tentador, y viable, la idea de relacionar los items del ComboBox con alguna instancia de la clase TPlayer. Para eso nos podemos valer del uso de AddItem() como AddObject().

 

Ahora bien, el problema está cuando agregamos el objeto al combo con cualquiera de esos dos métodos Add() le estamos diciendo al Combo que sea el "dueño parcial" (parcial en el sentido de que no es responsable de crear los objetos). ¿Y que implica esto? Básicamente: que luego un Combo.Items.Clear lo que hará es eliminar todos los strings que contenga y liberará todas las referencias de los objetos.  Conseguimos el mismo resultado si iteramos por la propiedad vectorial Objects[] y aplicamos un Free, y ni hablar del peor pecado: el asignarle el valor nil sin haber hecho un Free realmente.

 

¿Cómo le hacemos? En lugar de agregarle objetos, agreguemos texto (el cual debería obtenerse de cada Player obviamente), y deleguemos luego el trabajo de hacer una búsqueda de ese texto en la lista que TPlayerList mantiene. Entonces nuestro TPlayerList deberá tener un método FindPlayer() que regrese el Player, y otros tantos más. Puede ser incluso hasta útil que tenga una propiedad vectorial Players[]... El gusto de su diseño los dejo a sus criterios.

Esta forma de trabajar es más limpia:

1. Hacemos una mejor separación entre dominio y presentación. Tener nuestra TPlayerList nos independiza mucho, podemos representarla en muchos controles, y el que sea... combo, listbobox, frame, etc.

2. Centramos la administración de las instancias de nuestra clase TPlayer en una clase más dentro del dominio y evitamos problemas como el de lidiar con combos.AddObject()

 

TPlayerList puede usar internamente un TList, TObjectList, o cualquier "contenedor list" que uno quiera. Ahora bien, y yendo a lo que comentaba Fernando sobre un ejemplo usando TObjectList puedo decir que una "ventaja" que tiene es que tiene la particularidad de ser el dueño de la lista y hacer el trabajo sucio de liberar los objetos cuando éste se libere. Para ello cuenta con la propiedad de tipo boolean llamada OwnsObjects (que incluso puede ser inicializada en el constructor). Cuando es true el se encarga de iberar los objetos de la lista. Cuando es false es nuestra responsabilidad de hacer el trabajo. Esa es una de las diferencias sustanciales respecto a TList. Lo que tiene TObjectList es que ofrece una interfaz pensada y diseñada para interactuar con objetos mientras que TList es más abstracto y general y opera a nivel de punteros. Internamente TObjectList es un TList, que es a quien le delega el trabajo sucio.

 

El que se defina con genérico <T> no lo hace tan diferente. En esencia sigue siendo lo mismo, únicamente que ahora podemos meterle cualquier tipo. Para tal caso es más productivo directamente declarar un ObjectList<TPlayer> y con esto conseguimos que todo Add(), Delete(), Insert(), etc. acepte y trabaje con el tipo TPlayer.

 

Saludos,


  • 1

#37 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 13 marzo 2017 - 10:11

Recuerdo hace unos años que "yo ya sabia lo que eran objetos, las referencias a objetos, cuando estoy copiando una referencia, cuando estoy clonando una instancia, como manejar la memoria, etc"

 

Sin embargo, problemas como el que tuviste ahora, tenia todos los dias. En realidad esto pasa por lo que ya te comentamos en el hilo. Estas mezclando presentacion con dominio, y en lugar de "es mas facil", todo lo contrario, es mas dificil

 

Imaginate que en lugar de TList<T> tenes un TDataSet. Como llenas un combo a partir de un DataSet? En lineas generales:


delphi
  1. while not DataSet.Eof do
  2. begin
  3. ComboBox.Items.Add(DataSet.FieldByName('Campo').AsString);
  4. DataSet.Next;
  5. end;

Si fuera una lista:


delphi
  1. var
  2. Each: TPlayer;
  3. begin
  4. for Each in ListaPlayers do
  5. begin
  6. ComboBox.Items.Add(Each.Nombre);
  7. end;
  8. end;

Osea, el codigo para agregar es sencillo. Despues como haces la "sincronizacion" entre la lista y el combo es "problema tuyo", ya que podes hacerlo como quieras. A mi me gusta hacerlo por indice, es decir, el primer elemento del control visual se corresponde con el primero de la lista, y asi. Pero tambien podes hacer busquedas como dice Marcelo

 

Esto te brinda flexibilidad porque en realidad de que el form o control use la lista, es al reves, la lista usa el control visual y le agrega, saca, ordena, etc


  • 0

#38 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 14 marzo 2017 - 07:49

Recuerdo hace unos años que "yo ya sabia lo que eran objetos, las referencias a objetos, cuando estoy copiando una referencia, cuando estoy clonando una instancia, como manejar la memoria, etc"

 

Sin embargo, problemas como el que tuviste ahora, tenia todos los dias. En realidad esto pasa por lo que ya te comentamos en el hilo. Estas mezclando presentacion con dominio, y en lugar de "es mas facil", todo lo contrario, es mas dificil

 

Imaginate que en lugar de TList<T> tenes un TDataSet. Como llenas un combo a partir de un DataSet? En lineas generales:


delphi
  1. while not DataSet.Eof do
  2. begin
  3. ComboBox.Items.Add(DataSet.FieldByName('Campo').AsString);
  4. DataSet.Next;
  5. end;

Si fuera una lista:


delphi
  1. var
  2. Each: TPlayer;
  3. begin
  4. for Each in ListaPlayers do
  5. begin
  6. ComboBox.Items.Add(Each.Nombre);
  7. end;
  8. end;

Osea, el codigo para agregar es sencillo. Despues como haces la "sincronizacion" entre la lista y el combo es "problema tuyo", ya que podes hacerlo como quieras. A mi me gusta hacerlo por indice, es decir, el primer elemento del control visual se corresponde con el primero de la lista, y asi. Pero tambien podes hacer busquedas como dice Marcelo

 

Esto te brinda flexibilidad porque en realidad de que el form o control use la lista, es al reves, la lista usa el control visual y le agrega, saca, ordena, etc

 

Yo también puedo suelo hacerlo por índice... pero en ocasiones se necesita tenerlo ordenados por otros criterios, y por eso es que contemplo el uso de métodos de búsquedas.

 

Lo que puede resultar de interés en ciertas ocasiones, es aprovechar un objeto "link" que realice la sincronización entre presentación y el objeto. Para este ejemplo, en lugar de añadir objetos TPlayer, lo que hacemos es añadir objetos TPlayerLink. Esta nueva clase simplemente se encarga de apuntar y referenciar a la instancia de un TPlayer. El enfoque simple:


delphi
  1. constructor TPlayerLink.Create(APlayer: TPlayer);
  2. begin
  3. if Assigned(APlayer)
  4. then FPlayer := APlayer; // apuntamos al objeto
  5. end;
  6.  
  7. destructor TPlayerLink.Destroy;
  8. begin
  9. FPlayer := nil; // al destruir el enlace no liberamos, referenciamos a nil!
  10. end;

Entonces por cada instancia de TPlayer, tenemos una instancia de TPlayerLink que apunta a ésta. Al agregar al combo los PlayerLinks y al eliminarlos no estaremos eliminando las instancias de TPlayer, simplemente rompemos el enlace que podremos recrear después.

Lo que queda del análisis es determinar quien crea los TPlayerLink, el patrón Experto en Información sugiere que sea el mismo que el que crea los TPlayer, pero no necesariamente puede ser éste (sobre todo si ya hemos considerado el TPlayerList y además TPlayerLink es un objeto pensado más para la capa presentación)

 

Espero que se entienda.

 

Saludos,


  • 1

#39 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.419 mensajes
  • LocationRepública Dominicana

Escrito 14 marzo 2017 - 08:35

Gracias a ambos, me gusta el asunto del TObjectList, ojalá hubiese aprendido desde el principio a programar sobre POO, ahora mismo lo hago de manera procedural y funciones, aunque poco a poco voy aplicando el POO, pero por tiempo no me he puesto a estudiarlo a fondo. De todos modos abriré un hilo relacionado con la clase TPlayer en su momento e ir aprendiendo.

 

Saludos.


  • 1

#40 Agustin Ortu

Agustin Ortu

    Advanced Member

  • Moderadores
  • PipPipPip
  • 831 mensajes
  • LocationArgentina

Escrito 14 marzo 2017 - 09:42

Lo que puede resultar de interés en ciertas ocasiones, es aprovechar un objeto "link" que realice la sincronización entre presentación y el objeto. Para este ejemplo, en lugar de añadir objetos TPlayer, lo que hacemos es añadir objetos TPlayerLink. Esta nueva clase simplemente se encarga de apuntar y referenciar a la instancia de un TPlayer. 

 

Entonces por cada instancia de TPlayer, tenemos una instancia de TPlayerLink que apunta a ésta. Al agregar al combo los PlayerLinks y al eliminarlos no estaremos eliminando las instancias de TPlayer, simplemente rompemos el enlace que podremos recrear después.

Lo que queda del análisis es determinar quien crea los TPlayerLink, el patrón Experto en Información sugiere que sea el mismo que el que crea los TPlayer, pero no necesariamente puede ser éste (sobre todo si ya hemos considerado el TPlayerList y además TPlayerLink es un objeto pensado más para la capa presentación)

 

 

Si mal no recuerdo, dentro de la Vcl (sobre todo en algunos controles mas nativos y no tanto las envolturas de API de Windows) y sobre todo en FMX, se utiliza el mecanismo que describes

 

LiveBindings es otro ejemplo que pone un mediador entre la lista y el control

 

Yo por lo general empleo algo similar a MVP (Modelo Vista Presentador). 

 

En las versiones mas nuevas de FMX, calculo que XE7 o mas, existe un nuevo enfoque para los controles, en donde se implementa un modelo que uno de los desarrolladores de FMX llamo "Control Model Presentation"

 

En su blog hay mas informacion. Esta bastante bien explicado, hay hasta diagramas UML que explican como se relacionan los distintos components

 

Parte 1: http://yaroslavbrovi...tion-part-1-en/

Parte 2: http://yaroslavbrovi...utocomplete-en/

 

Hay un biblioteca publicada por el en donde se pueden ver ejemplos de como usar este enfoque (link en ruso, traducir la pagina a ingles): http://fire-monkey.r...load-versions/ 

 

Los controles que derivan de TPresentedControl siguen este enfoque. Hay varios patrones mas que entran en juego en esta implementacion. Algunos controles tambien tienen un proxy que se el que realmente hace la presentacion. Algunos se comunican con su modelo de manera "directa", con eventos y propiedades. Otros usan mensajes 

 

Esto es lo que aparece en las notas de versiones, por ejemplo en 10.1 Berlin

 

 

 

Grid Improvements

The presentation logic and the data model of Grid controls have been detached from these. This is a necessary, API-breaking change that makes it possible to add native presentation support to grid controls in the future, among other improvements. See Changes to FireMonkey Grid Controls in Berlin.

 

Mas informacion aca: http://docwiki.embar...trols_in_Berlin


  • 0




IP.Board spam blocked by CleanTalk.