Cómo liberar un componente en Android / iOS

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

 edit := TEdit.Create(Self); 

Quiero liberarla usando edit.Free , pero todavía apenas en forma.

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

Lo mismo parece suceder no sólo para TEdit, sino para cualquier componente que utilice Android o iOS.

Respuesta corta

Hay dos reglas que deben seguirse al liberar cualquier objeto descendente de TComponent en los compiladores de Delphi ARC (actualmente Android e iOS):

  • El uso de DisposeOf es obligatorio independientemente del objeto que tenga el propietario o no
  • En los destructores o en los casos en que la referencia no va a salir del ámbito de aplicación poco después de que DisposeOf se llame, la referencia de objeto también debe establecerse a nil (explicación detallada en Pitfalls)

Puede resultar atractivo tener el método DisposeOfAndNil , pero ARC lo hace mucho más complicado de lo que era el caso con el antiguo método FreeAndNil y sugeriría usar la secuencia DisposeOf - nil para evitar problemas adicionales:

 Component.DisposeOf; Component := nil; 

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

DisposeOf en el contexto de la gestión de memoria ARC

DisposeOf rompe ARC. Esto viola la regla de oro de ARC Cualquier referencia de objeto puede ser una referencia de objeto válida o nula e introduce una tercera referencia de objeto "zombie" con disposición estatal.

Cualquier persona que intenta entender la gestión de memoria ARC debe mirar a DisposeOf como la adición que sólo resuelve Delphi problemas de marco específico y no concepto que realmente pertenece a ARC en sí.

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

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

Además Free Notification mecanismo de Free Notification hay otros diseños similares en marcos de Delphi adecuados para la gestión de memoria manual porque se basan en romper ciclos de referencia fuertes en destructor, pero esos diseños no son adecuados para ARC.

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

Una cosa hay que señalar aquí. Cualquier código que utiliza o hereda de TComponent automáticamente se convierte en código heredado en el contexto de la administración ARC adecuada, incluso si la escribe hoy.

Cita del blog de Allen Bauer Entregue al lado de ARC

Entonces, ¿qué otra cosa DisoseOf resolver? Es muy común entre varios marcos de Delphi (VCL y FireMonkey incluidos), colocar el código activo de notificación o de gestión de listas dentro del constructor y destructor de una clase. El modelo propietario / propietario de TComponent es un ejemplo clave de tal diseño. En este caso, el diseño del marco de componentes existente se basa en muchas actividades distintas de la simple "gestión de recursos" que se producirán en el destructor.

TComponent.Notification () es un ejemplo clave de tal cosa. En este caso, la forma correcta de "desechar" un componente, es usar DisposeOf. Un derivado TComponent no suele ser una instancia transitoria, sino que es un objeto de vida más larga que también está rodeado por un sistema completo de otras instancias componentes que componen cosas como formas, marcos y datamodules. En este caso, el uso de DisposeOf es apropiado.

Cómo funciona DisposeOf

Para tener una mejor comprensión de lo que sucede exactamente cuando se llama a DisposeOf , es necesario saber cómo funciona el proceso de destrucción de objetos de Delphi.

Hay tres fases distintas involucradas en la liberación de objetos tanto en ARC como en compiladores Delphi no ARC

  1. Destructor destructor Destroy cadena de métodos
  2. Limpieza de campos administrados por objetos: cadenas, interfaces, matrices dinámicas (en el compilador ARC que incluye referencias de objetos simples, también)
  3. Liberar memoria de objetos del montón

Liberación de objetos con compiladores que no son ARC

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

Liberación de objetos con compiladores ARC

  • Component.Free o Component := nil -> disminuye el recuento de referencia del objeto seguido de a) o b)

    • A) si el recuento de referencia del objeto es 0 -> ejecución inmediata de las etapas 1 -> 2 -> 3
    • B) si el recuento de referencia de objeto es mayor que 0, nada más sucede

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

Sistema de notificación TComponent

Free Notification mecanismo de notificación Free Notification TComponent Free Notification a los componentes registrados que se está liberando la instancia de componente particular. Los componentes notificados pueden manejar esa notificación dentro del método de Notification virtual y asegurarse de que eliminan todas las referencias que pueden tener sobre el componente que se está destruyendo.

Bajo compiladores que no son ARC, este mecanismo garantiza que no terminen con punteros que apuntan a objetos no válidos y bajo compiladores ARC, borrar las referencias al componente de destrucción disminuirá su recuento de referencia y romperá ciclos de referencia fuertes.

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

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

Por ejemplo, si tienes TEdit y TPopupMenu en tu formulario y asignas ese menú emergente para editar la propiedad PopupMenu , editará una fuerte referencia al menú emergente en su campo FEditPopupMenu y el menú emergente contendrá una fuerte referencia para editar en su lista FFreeNotifies . Si desea liberar cualquiera de estos dos componentes tiene que llamar a DisposeOf o simplemente continuará existiendo.

Si bien puede intentar rastrear esas conexiones manualmente y romper ciclos de referencia fuertes antes de liberar cualquiera de los objetos que pueden no ser tan fáciles de hacer en la práctica.

El siguiente código básicamente fuga ambos componentes bajo ARC porque mantendrán una referencia fuerte el uno al otro y, una vez finalizado el procedimiento, ya no tendrá referencias 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 referencia 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 DisposeOf

Además de romper ARC, eso es malo por sí solo, porque cuando lo rompes no tienes mucho uso de él, también hay dos problemas importantes con cómo DisposeOf se implementa que los desarrolladores deben ser conscientes.

1. DisposeOf no disminuye el recuento de referencia en la referencia de llamada Informe QP 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 las referencias de tipos administrados internos de la instancia Referencia 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 las cuestiones anteriores puede manifestarse de diferentes maneras. De mantener más memoria asignada de lo necesario a difícil de atrapar bugs que son causados ​​por el conteo de referencia incorrecto, inesperado de objetos no pertenecientes contenidos y referencias de interfaz.

Dado que DisposeOf no disminuye el recuento de referencia de referencia de llamada, es importante nil dicha referencia en destructores, de lo contrario las jerarquías de objetos enteros pueden permanecer vivas mucho más tiempo de lo necesario y en algunos casos incluso durante toda la vida útil de la aplicación.

3. DisposeOf no se puede utilizar para resolver todas las referencias circulares

El último, pero no menos importante, con DisposeOf es que romperá las referencias circulares sólo si hay código en el destructor que las resuelve, como lo TComponent sistema de notificación TComponent .

Estos ciclos que no son manejados por el destructor deben ser rotos utilizando atributos [weak] y / o [unsafe] en una de las referencias. Esa también es la práctica ARC preferida.

DisposeOf no debe utilizarse como solución rápida para romper todos los ciclos de referencia (los que nunca fue diseñado para), ya que no funcionará y abusar de ella puede dar lugar a difíciles de detectar pérdidas de memoria.

Ejemplo simple de ciclo que no será roto por 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; 

Por encima del código se verán tanto las instancias de objeto primario como secundario. Combinado con el hecho de que DisposeOf no borra los tipos administrados internos (incluidas las cadenas), esas filtraciones pueden ser enormes dependiendo del tipo de datos que se estén almacenando en el interior. La única manera (correcta) de romper ese ciclo es cambiando la declaración de clase TChild :

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

En las plataformas móviles la vida útil se gestiona mediante ARC. Los objetos sólo se destruyen cuando no hay referencias al objeto que queda. Su objeto tiene referencias a él, específicamente de su padre.

Ahora puede usar DisposeOf para forzar el objeto a ser 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 referencias al objeto. Quitarlo de su recipiente. Por ejemplo, estableciendo su padre a ser cero.

  • Convertir aplicaciones iPhone / iPad en Android
  • React Native v Formularios Xamarin - Elegir el entorno de la aplicación multiplataforma
  • Encuentra mi versión de aplicación de aire en AS3 en iOS y Android
  • `OnEndReached` no se dispara en ScrollView animado
  • Motor de juegos multiplataforma 2D para Android e iOS?
  • Algo similar a XNA para Android o IOS?
  • cordova notificación local sonido no funciona en ios y andorid
  • ¿Cuál es la mejor manera de incluir un número de compilación para el seguimiento de Google Analytics de la aplicación nativa?
  • Accede a CloudKit desde Android?
  • "URL con esquema personalizado" en datos de iOS y "URL" en datos de Android en facebook
  • Marco de realidad aumentada basado en localización (Android, iOS) con POIs 3D?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.