¿Qué tipo de fugas evita o minimiza el conteo automático de referencias en Objective-C?

En las plataformas Mac e iOS, las memory leaks a menudo son causadas por punteros inéditos. Tradicionalmente, siempre ha sido de sum importancia verificar sus allocs, copias y retenciones para asegurarse de que cada uno tenga su correspondiente mensaje de lanzamiento.

La herramienta que viene con Xcode 4.2 introduce el recuento automático de referencias (ARC) con la última versión del comstackdor LLVM , que elimina por completo este problema al hacer que el comstackdor guarde en memoria tus cosas. Eso es genial, y reduce un montón de tiempo de desarrollo innecesario y mundano y evita una gran cantidad de pérdidas de memoria descuidadas que son fáciles de solucionar con un equilibrio adecuado de retención / liberación. Incluso los grupos de autorelease deben administrarse de manera diferente cuando habilita ARC para sus aplicaciones Mac e iOS (ya que no debe asignar su propio NSAutoreleasePool más).

Pero, ¿qué otra pérdida de memoria no impide que tenga que estar pendiente?

Como beneficio adicional, ¿cuáles son las diferencias entre ARC en Mac OS X e iOS y la recolección de basura en Mac OS X?

El principal problema relacionado con la memoria del que aún deberá tener conocimiento es retener ciclos. Esto ocurre cuando un objeto tiene un puntero fuerte a otro, pero el objeto objective tiene un puntero fuerte hacia el original. Incluso cuando se eliminan todas las demás referencias a estos objetos, aún se conservarán entre sí y no se liberarán. Esto también puede ocurrir indirectamente, mediante una cadena de objetos que podría tener el último en la cadena que hace referencia a un objeto anterior.

Es por esta razón que existen los calificadores __unsafe_unretained y __weak propiedad. El primero no retendrá ningún objeto al que apunte, pero deja abierta la posibilidad de que ese objeto se vaya y señale a la mala memoria, mientras que el último no retiene el objeto y automáticamente se establece en cero cuando su objective es desasignado. De los dos, __weak generalmente se prefiere en las plataformas que lo soportan.

Utilizaría estos calificadores para cosas como delegates, donde no desea que el objeto retenga su delegado y potencialmente conduzca a un ciclo.

Otro par de preocupaciones importantes relacionadas con la memoria son el manejo de objetos Core Foundation y la memoria asignada usando malloc() para tipos como char* . ARC no administra estos tipos, solo objetos Objective-C, por lo que aún tendrá que lidiar con ellos usted mismo. Los tipos de Fundamentos básicos pueden ser particularmente complicados, porque a veces deben enlazarse para coincidir con objetos Objective-C, y viceversa. Esto significa que el control debe transferirse de ARC hacia adelante y hacia atrás cuando se realiza un puente entre los tipos de CF y Objective-C. Algunas palabras clave relacionadas con este puente se han agregado, y Mike Ash tiene una gran descripción de varios casos de puente en su larga redacción de ARC .

Además de esto, hay varios otros casos menos frecuentes, pero aún potencialmente problemáticos, que la especificación publicada entra en detalle.

Gran parte del nuevo comportamiento, basado en mantener los objetos mientras haya un fuerte puntero hacia ellos, es muy similar a la recolección de basura en la Mac. Sin embargo, los fundamentos técnicos son muy diferentes. En lugar de tener un proceso de recolector de basura que se ejecuta a intervalos regulares para limpiar objetos que ya no se apuntan, este estilo de administración de la memoria se basa en las reglas rígidas de retención / liberación que todos debemos obedecer en Objective-C.

ARC simplemente toma las tareas repetitivas de administración de memoria que hemos tenido que hacer durante años y las descarga al comstackdor para que no tengamos que preocuparnos por ellas nuevamente. De esta forma, no tiene los problemas de detención o los perfiles de memoria de diente de sierra experimentados en las plataformas recolectadas. He experimentado ambos en mis aplicaciones de Mac recolectadas con basura, y estoy ansioso por ver cómo se comportan bajo ARC.

Para obtener más información sobre la recolección de basura en comparación con ARC, vea esta respuesta muy interesante de Chris Lattner en la lista de correo de Objective-C , donde enumera muchas ventajas de ARC sobre la recolección de basura Objective-C 2.0. Me encontré con varios de los problemas de GC que describe.

ARC no lo ayudará con la memoria que no es ObjC, por ejemplo, si malloc() algo, aún necesita free() .

ARC puede ser engañado por performSelector: si el comstackdor no puede determinar qué es el selector (el comstackdor generará una advertencia al respecto).

ARC también generará código siguiendo las convenciones de nomenclatura de ObjC, de modo que si mezcla el código ARC y MRC puede obtener resultados sorprendentes si el código MRC no hace lo que el comstackdor cree que prometen los nombres.

Experimenté pérdidas de memoria en mi aplicación debido a los siguientes 4 problemas:

  1. No invalida NSTimers al descartar controladores de vista
  2. Olvidarse de eliminar cualquier observador en NSNotificationCenter al descartar el controlador de vista.
  3. Mantener fuertes referencias a uno mismo en bloques.
  4. Uso de referencias sólidas a los delegates en las propiedades del controlador de vista

Afortunadamente, me encontré con la siguiente publicación de blog y pude corregirlos: http://www.reigndesign.com/blog/debugging-retain-cycles-in-objective-c-four-likely-culprits/

ARC tampoco administrará los tipos de CoreFoundation. Puede ‘puentearlos’ (usando CFBridgingRelease() ) pero solo si va a usarlo como un objeto Objective-C / Cocoa. Tenga en cuenta que CFBridgingRelease solo disminuye el CoreFoundation retenga el conteo por 1 y lo mueve a Objective-C’s ARC.

Xcode 9 proporciona una gran herramienta para encontrar ese tipo de problemas. Se llama: ” Debug Memory Graph “. Utilizándolo, puedes encontrar el objeto que se filtró por tipo de clase y puedes ver claramente quién tiene una referencia fuerte de él, liberándolo desde allí resuelve tu problema. También detecta ciclos de memoria.

Ver más información sobre cómo usarlo