Cómo liberar un componente en Android / iOS

TEdit dinámicamente un TEdit en un formulario en Android:

 edit := TEdit.Create(Self); 

Quiero liberarlo usando edit.Free , pero todavía está en el formulario.

Este código funciona bien en win32, pero falló en Android.

Lo mismo parece ocurrir no solo para TEdit, sino también para cualquier componente que use Android o iOS.

Respuesta corta

Hay dos reglas que se deben seguir al liberar cualquier object descendiente de TComponent en los comstackdores de Delphi ARC (actualmente Android e iOS):

  • usar DisposeOf es obligatorio independientemente de que el object tenga propietario o no
  • en los destructores o en los casos en que la reference no se sale del scope poco después de que se DisposeOf , la reference de object también debe establecerse en nil (explicación detallada en las trampas)

Puede ser atractivo tener el método DisposeOfAndNil , pero ARC lo hace mucho más complicado de lo que era el caso con el método anterior de FreeAndNil y sugiero usar una DisposeOf - nil simple de DisposeOf - nil para evitar problemas adicionales:

 Component.DisposeOf; Component := nil; 

Si bien en muchos casos el código funcionará correctamente incluso si no se siguen las reglas anteriores, dicho código sería bastante frágil y podría romperse fácilmente con otro código introducido en lugares aparentemente no relacionados.

DisposeOf en el context de la administración de memory ARC

DisposeOf rompe ARC. Viola la regla dorada de ARC Cualquier reference de object puede ser reference de object válida o nula e introduce una reference de object "zombie" de tercer estado.

Cualquier persona que intente comprender la gestión de la memory ARC debería considerar DisposeOf como una adición que solo resuelve los problemas del marco específico de Delphi y no el concepto que realmente pertenece a ARC.

¿Por qué existe DisposeOf en los comstackdores de Delphi ARC?

TComponent class TComponent (y todos sus descendientes) fue diseñada teniendo en count la gestión manual de la memory. Utiliza un mecanismo de notificación que no es compatible con la administración de memory ARC porque se basa en romper fuertes ciclos de reference en el destructor. Como TComponent es una de las classs base en las que se basan los frameworks de Delphi, debe ser capaz de funcionar correctamente bajo la administración de memory ARC.

Además Free Notification mecanismo de Free Notification , existen otros layouts similares en los frameworks de Delphi adecuados para la gestión manual de la memory, ya que se basan en romper fuertes ciclos de reference en el destructor, pero esos layouts no son adecuados para ARC.

DisposeOf método DisposeOf permite la llamada directa del destructor de objects y permite que dicho código henetworkingado se reproduzca junto con ARC.

Una cosa debe anotarse aquí. Cualquier código que use o henetworkinge de TComponent se convierte automáticamente en código henetworkingado en el context de la administración adecuada de ARC, incluso si lo escribe hoy.

Cita del blog de Allen Bauer Entrégate al lado del ARC

Entonces, ¿qué más soluciona DisoseOf? Es muy común entre varios frameworks Delphi (VCL y FireMonkey incluidos), colocar una notificación activa o un código de gestión de lists dentro del constructor y destructor de una class. El model Owner / Owned de TComponent es un ejemplo key de dicho layout. En este caso, el layout del marco de componentes existente depende de muchas actividades diferentes a la simple "gestión de resources" que sucederá en el destructor.

TComponent.Notification () es un ejemplo key de tal cosa. En este caso, la forma correcta de "disponer" un componente es usar DisposeOf. Un derivado de TComponent no suele ser una instancia transitoria, sino que es un object de vida más larga que también está rodeado por un sistema completo de otras instancias de componentes que componen cosas tales como forms, frameworks y modules de datos. En esta instancia, usar DisposeOf es apropiado.

Cómo funciona DisposeOf

Para comprender mejor qué sucede exactamente cuando se llama a DisposeOf , es necesario saber cómo funciona el process de destrucción de objects de Delphi.

Hay tres etapas distintas implicadas en la liberación de objects en los comstackdores ARC y no ARC Delphi

  1. Destripador llamante destructor Destroy cadena de methods
  2. limpieza de campos gestionados de objects: cadenas, interfaces, matrices dinámicas (en el comstackdor ARC que incluye también references de object plano)
  3. liberando memory de objects del montón

Liberación de objects con comstackdores no ARC

Component.Free -> ejecución inmediata de las etapas 1 -> 2 -> 3

Lanzamiento de object con comstackdores ARC

  • Component.Free o Component := nil -> disminuye el recuento de reference de object seguido de a) ob)

    • a) si el recuento de reference del object es 0 -> ejecución inmediata de las etapas 1 -> 2 -> 3
    • b) si el recuento de reference del object es mayor que 0, nada más sucede

  • Component.DisposeOf -> la ejecución inmediata de la etapa 1 , las etapas 2 y 3 se ejecutará más tarde cuando el recuento de reference de objects llegue a 0. DisposeOf no disminuye el recuento de reference de la reference de llamada.

Sistema de notificación de TComponent

Free Notification mecanismo de notificación Free Notification TComponent Free Notification a los componentes registrados que la instancia del componente en particular se está liberando. Los componentes notificados pueden manejar esa notificación dentro del método de Notification virtual y asegurarse de que borran todas las references que pueden contener en el componente que se destruye.

En los comstackdores que no pertenecen a ARC, ese mecanismo garantiza que no termine con pointers colgantes que apunten a objects liberados no válidos y, en el caso de los comstackdores ARC, eliminar las references al componente destructor networkingucirá su conteo de references y romperá los ciclos de reference fuertes.

Free Notification mecanismo de Free Notification se está TComponent en el destructor de TComponent y sin DisposeOf y la ejecución directa del destructor, dos componentes podrían mantener fuertes references DisposeOf manteniéndose con vida durante toda la vida útil de la aplicación.

FFreeNotifies list FFreeNotifies que contiene la list de componentes interesados ​​en la notificación se declara como FFreeNotifies: TList<TComponent> y almacenará una fuerte reference a cualquier componente registrado.

Por ejemplo, si tiene TEdit y TPopupMenu en su formulario y le asigna ese menu emergente para editar la propiedad PopupMenu , la edición tendrá una fuerte reference al menu emergente en su campo FEditPopupMenu , y el menu emergente tendrá una fuerte reference para editar en su list FFreeNotifies . Si desea liberar cualquiera de esos dos componentes, debe llamar a DisposeOf o seguirán existiendo.

Si bien puede intentar rastrear esas conexiones manualmente y romper fuertes ciclos de reference antes de liberar cualquiera de esos objects que tal vez no sean tan fáciles de hacer en la práctica.

El código siguiente básicamente perderá ambos componentes en ARC porque mantendrán una fuerte reference entre ellos y, una vez finalizado el procedimiento, ya no tendrá references externas que apunten a ninguno de esos componentes. Sin embargo, si reemplaza Menu.Free con Menu.DisposeOf activará el mecanismo de Free Notification y romperá el ciclo de reference fuerte.

 procedure ComponentLeak; var Edit: TEdit; Menu: TPopupMenu; begin Edit := TEdit.Create(nil); Menu := TPopupMenu.Create(nil); Edit.PopupMenu := Menu; // creating strong reference cycle Menu.Free; // Menu will not be released because Edit holds strong reference to it Edit.Free; // Edit will not be released because Menu holds strong reference to it end; 

Trampas de disponer deOf

Además de romper ARC, eso es malo por sí mismo, porque cuando lo rompes no tienes mucho uso de él, también hay dos problemas importantes con cómo se implementa DisposeOf que los desarrolladores deberían tener en count.

1. DisposeOf no disminuye el recuento de reference en el informe QP de reference de llamada RSP-14681

 type TFoo = class(TObject) public a: TObject; end; var foo: TFoo; b: TObject; procedure DoDispose; var n: integer; begin b := TObject.Create; foo := TFoo.Create; foo.a := b; foo.DisposeOf; n := b.RefCount; // foo is still alive at this point, also keeping b.RefCount at 2 instead of 1 end; procedure DoFree; var n: integer; begin b := TObject.Create; foo := TFoo.Create; foo.a := b; foo.Free; n := b.RefCount; // b.RefCount is 1 here, as expected end; 

2. DisposeOf no limpia la instancia de types gestionados internos references Informe QP RSP-14682

 type TFoo = class(TObject) public s: string; d: array of byte; o: TObject; end; var foo1, foo2: TFoo; procedure DoSomething; var s: string; begin foo1 := TFoo.Create; foo1.s := 'test'; SetLength(foo1.d, 1); foo1.d[0] := 100; foo1.o := TObject.Create; foo2 := foo1; foo1.DisposeOf; foo1 := nil; s := IntToStr(foo2.o.RefCount) + ' ' + foo2.s + ' ' + IntToStr(foo2.d[0]); // output: 1 test 100 - all inner managed references are still alive here, // and will live until foo2 goes out of scope end; 

solución alternativa

 destructor TFoo.Destroy; begin s := ''; d := nil; o := nil; inherited; end; 

El efecto combinado de los problemas anteriores puede manifestarse de diferentes maneras. De mantener más memory asignada de la necesaria para los errores difíciles de detectar que son causados ​​por un conteo de references incorrecto e inesperado de references de interfaces y objects no poseídos.

Dado que DisposeOf no disminuye el recuento de reference de la reference de llamada, es importante no tener dicha reference en los destructores; de lo contrario, las jerarquías de object completo pueden mantenerse activas durante mucho más time del necesario y, en algunos casos, incluso durante toda la vida útil de la aplicación.

3. DisposeOf no se puede usar para resolver todas las references circulares

Por último, pero no less importante, el problema con DisposeOf es que romperá las references circulares solo si hay un código en el destructor que las resuelva, como lo TComponent sistema de notificación de TComponent .

Dichos ciclos que no sean manejados por destructor deberían romperse usando attributes [weak] y / o [unsafe] en una de las references. Esa también es la práctica preferida de ARC.

DisposeOf no debe usarse como solución rápida para romper todos los ciclos de reference (para los que nunca fue diseñado) porque no funcionará y si se abusa puede resultar difícil rastrear las memory leaks.

El ejemplo simple de ciclo que DisposeOf no DisposeOf es:

 type TChild = class; TParent = class(TObject) public var Child: TChild; end; TChild = class(TObject) public var Parent: TParent; constructor Create(AParent: TParent); end; constructor TChild.Create(AParent: TParent); begin inherited Create; Parent := AParent; end; var p: TParent; begin p := TParent.Create; p.Child := TChild.Create(p); p.DisposeOf; p := nil; end; 

El código anterior perderá las instancias de objects secundarios y secundarios. Combinado con el hecho de que DisposeOf no borra los types gestionados internos (incluidas las cadenas), esas pérdidas pueden ser enormes dependiendo del tipo de datos que esté almacenando en el interior. La única forma (apropiada) de romper ese ciclo es cambiando la statement de class de TChild :

  TChild = class(TObject) public [weak] var Parent: TParent; constructor Create(AParent: TParent); end; 

En la vida útil de las plataforms mobilees se gestiona usando ARC. Los objects solo se destruyen cuando no hay references al object restante. Su object tiene references a él, específicamente de su padre.

Ahora puedes usar DisposeOf para forzar que el object sea destruido. Más detalles aquí: http://blogs.embarcadero.com/abauer/2013/06/14/38948

Sin embargo, sospecho que una mejor solución sería eliminar las references al object. Retírelo de su contenedor. Por ejemplo, configurando su padre para que sea nulo.

He intentado todo lo anterior y no he llegado a ninguna parte. Mi última opción fue posiblemente una que generará una serie de comentarios de odio, pero la única solución posible que funcionó al final para mí.

Tengo una scrollbox y agrego los componentes que hice yo mismo (derivados de TPanel), en su mayoría 50 a la vez. Mi mejor solución fue esta:

Antes de crear el segundo nivel de elementos:

 Scrollbox1.align := TAlignLayout.none; Scrollbox1.width := 0; Scrollbox1.position.y := 5000; scrollbox1.visible := false; scrollbox1.name := 'Garbage' + inttostr(garbageitemscount); garbageitemscount := garbageitemscount + 1; scrollbox1 := TScrollbox.create(nil); scrollbox1.parent := ContainerPanel; Scrollbox1.align := TAlignLayout.client; 

Al crear los componentes, su padre está configurado en Scrollbox1 de nuevo .. Esto da la ilusión de que los elementos se han ido cuando, de hecho, simplemente se salen de la pantalla hasta que la aplicación está cerrada y luego son liberados por Android.

No puedo creer que este enfoque sea factible para aplicaciones de mayor escala, pero al final mis necesidades son adecuadas. Para aquellos que tampoco saben qué hacer. Component.DisposeOf – Crashed App Component.Free – Sin efecto alguno.

De hecho, de acuerdo con mi request:

 Component := TComponent.create(ScrollBox1); Component.Parent := ScrollBox1; showmessage(inttostr(ScrollBox1.ChildrenCount)); //<-- This says 0, even tho 50 Components are present