Muy buenas, me llamo Miguel y aquí les traigo un nuevo post.
Índice
Encontrar y romper ciclos de retención
Hay muchas razones para que el código funcione de manera óptima. En un post, te he mostrado cómo usar el Time Profiler para medir el tiempo empleado en cada método de tu código, y cómo analizar los resultados.
Si bien se pueden descubrir, analizar y solucionar muchos problemas relacionados con el rendimiento con estas herramientas, el uso de la memoria a menudo se debe depurar de manera ligeramente diferente. Especialmente si está relacionado con pérdidas de memoria.
En la publicación de hoy, te mostraré cómo usar la herramienta de gráfico de memoria en Xcode para analizar los objetos que se guardan en la memoria de tu aplicación y cómo usar esta herramienta para descubrir pérdidas de memoria. Me enfocaré específicamente en retener ciclos hoy.
Activar el gráfico de memoria
Cuando ejecuta su aplicación con Xcode, puede hacer clic en el icono del depurador de memoria que se encuentra entre su código y la consola, o en la parte inferior de la ventana de Xcode si no tiene la consola abierta:
Al hacer clic en este icono, Xcode tomará una instantánea del gráfico de memoria de su aplicación y las relaciones que cada objeto tiene con otros objetos.
La ejecución de su aplicación se detendrá y Xcode le mostrará todos los objetos que están actualmente en la memoria. Tenga en cuenta que esto puede tardar un poco, dependiendo del tamaño de su aplicación.
En la barra lateral del lado izquierdo, Xcode muestra una lista completa de todos los objetos que ha descubierto.
Cuando selecciona un objeto en la barra lateral, la sección central mostrará su objeto seleccionado y las relaciones que tiene con otros objetos. A veces, es un gráfico grande, como en la captura de pantalla. Otras veces, es un gráfico más pequeño con solo un par de objetos.
Si Xcode detecta una relación que sospecha que es una pérdida de memoria o un ciclo de retención, agregará un cuadrado morado con un signo de interrogación detrás del objeto en la barra lateral.
En la captura de pantalla que acaba de ver, es bastante obvio dónde están los cuadrados morados. Si están más ocultos, o simplemente desea filtrar las pérdidas de memoria, puede hacerlo usando el menú de filtro en la parte inferior de la barra lateral como se muestra en la siguiente captura de pantalla:
La captura de pantalla anterior muestra que las instancias de dos objetos diferentes se mantienen en la memoria, mientras que Xcode cree que no deberían hacerlo. Cuando hace clic en uno de ellos, el problema se vuelve visible de inmediato.
El DataProvider
y DetailPage
en este ejemplo se apuntan entre sí. Un ejemplo clásico de ciclo de retención. Veamos cómo ocurre esto y qué puede hacer para solucionarlo.
Entender cómo ocurren los ciclos de retención y cómo solucionarlos
En iOS, los objetos se eliminan de la memoria cuando no hay otros objetos que mantengan una fuerte referencia a ellos. Cada instancia de un objeto que crea en su aplicación tiene un recuento de retención.
Cada vez que pasa una referencia a su objeto a un lugar diferente en su código, su recuento de retención aumenta porque ahora hay un objeto más apuntando a la ubicación en la memoria para ese objeto.
Este principio de retención de recuentos se aplica principalmente a las clases. Porque, cuando pasa una instancia de una clase en su código, realmente está pasando una referencia de memoria, lo que significa que varios objetos apuntan a la misma dirección de memoria.
Cuando pasa tipos de valor, el valor se copia cuando se pasa. Esto significa que el recuento de retención para un tipo de valor suele ser siempre uno; nunca hay más de un objeto apuntando a la dirección de memoria de un tipo de valor.
Para que un objeto se elimine de la memoria, su recuento de referencias debe ser cero; ningún objeto debe hacer referencia a esa dirección en la memoria.
Cuando dos objetos tienen una referencia entre sí, que suele ser el caso cuando se trabaja con delegados, es posible que el recuento de referencias para cualquiera de los objetos nunca llegue a cero porque mantienen una referencia entre sí.
Tenga en cuenta que mencioné un strong
referencia al principio de esta sección. Hice eso a propósito, si tenemos una referencia fuerte, seguramente existe algo como un weak
referencia ¿verdad? ¡Ahi esta!
Las referencias débiles son referencias a instancias de tipos de referencia que no aumentan el recuento de referencias para el objeto al que apuntan. Los principios que se aplican aquí son exactamente los mismos que se utilizan yo débil en cierres.
Haciendo la propiedad delegada de un objeto weak
, el delegado y su propietario no se mantienen vivos y ambos objetos se pueden desasignar. En el ejemplo que estábamos viendo, esto significa que necesitamos cambiar el siguiente código:
class DataProvider { var delegate: DataDelegate? // rest of the code }
En lo siguiente:
class DataProvider { weak var delegate: DataDelegate? // rest of the code }
Para que esto funcione, DataDelegate
debe estar limitado a ser una clase, puede hacer esto agregando : AnyObject
a su declaración de protocolo. Por ejemplo:
protocol DataDelegate: AnyObject { // requirements }
Cuando ejecute la aplicación nuevamente y use el gráfico de memoria para buscar ciclos de retención, notará que no hay más cuadrados de color púrpura y el gráfico de memoria se ve exactamente como lo esperaría.
En resumen
En este artículo, le he mostrado que puede usar Xcode para visualizar y explorar el gráfico de memoria de su aplicación. Esto le ayuda a encontrar pérdidas de memoria y retener ciclos.
Al hacer clic en un objeto que está en la memoria, puede explorar su relación con otros objetos y, en última instancia, puede rastrear los ciclos de retención. También aprendió qué es un ciclo de retención, cómo ocurren y cómo puede romperlos.
Gracias por leer hasta el final.
Añadir comentario