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

235

En las plataformas Mac e iOS, las pérdidas de memoria a menudo son causadas por punteros inéditos. Tradicionalmente, siempre ha sido de suma importancia verificar sus asignaciones, copias y retenciones para asegurarse de que cada una tenga un mensaje de liberación correspondiente.

La cadena de herramientas que viene con Xcode 4.2 presenta el conteo automático de referencias (ARC) con la última versión del compilador LLVM , que elimina por completo este problema al hacer que el compilador administre la memoria por usted. Eso es bastante bueno, y reduce mucho tiempo de desarrollo innecesario y mundano y evita muchas pérdidas de memoria descuidadas que son fáciles de solucionar con el equilibrio adecuado de retención / liberación. Incluso los grupos de liberación automática deben administrarse de manera diferente cuando habilita ARC para sus aplicaciones Mac e iOS (ya que ya no debería asignar sus propios NSAutoreleasePools).

Pero, ¿qué otras pérdidas de memoria no evitan que aún tenga que tener cuidado?

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?

BoltClock
fuente

Respuestas:

262

El principal problema relacionado con la memoria que aún deberá conocer es conservar los ciclos. Esto ocurre cuando un objeto tiene un puntero fuerte a otro, pero el objeto de destino tiene un puntero fuerte de vuelta al original. Incluso cuando se eliminan todas las demás referencias a estos objetos, aún se mantendrán entre sí y no se liberarán. Esto también puede suceder indirectamente, por una cadena de objetos que podrían tener el último en la cadena refiriéndose a un objeto anterior.

Es por esta razón que existen los calificadores de propiedad __unsafe_unretainedy __weak. El primero no retendrá ningún objeto al que apunte, pero deja abierta la posibilidad de que ese objeto desaparezca y apunte a mala memoria, mientras que el segundo no retiene el objeto y se establece automáticamente en cero cuando su objetivo se desasigna. De los dos, __weakgeneralmente se prefiere en plataformas que lo admiten.

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

Otro par de preocupaciones importantes relacionadas con la memoria son el manejo de los objetos de Core Foundation y la memoria asignada usando malloc()tipos como char*. ARC no gestiona estos tipos, solo objetos Objective-C, por lo que aún tendrá que lidiar con ellos usted mismo. Los tipos de Core Foundation pueden ser particularmente complicados, porque a veces necesitan ser puenteados para que coincidan con los objetos Objective-C, y viceversa. Esto significa que el control debe transferirse de un lado a otro desde ARC cuando se establece un puente entre los tipos de CF y Objective-C. Se han agregado algunas palabras clave relacionadas con este puente, y Mike Ash tiene una excelente descripción de varios casos de puente en su larga redacción ARC .

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

Gran parte del nuevo comportamiento, basado en mantener los objetos siempre que haya un puntero fuerte para 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 recolección de basura que se ejecuta a intervalos regulares para limpiar objetos que ya no se apuntan, este estilo de administración de memoria se basa en las rígidas reglas 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 compilador para que nunca tenga que preocuparnos por ellas nuevamente. De esta manera, no tiene los problemas de detención o los perfiles de memoria de diente de sierra experimentados en las plataformas de recolección de basura. He experimentado ambos en mis aplicaciones Mac recolectadas de basura, y estoy ansioso por ver cómo se comportan bajo ARC.

Para más información sobre la recolección de basura vs. 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 de Objective-C 2.0. Me he encontrado con varios de los problemas de GC que describe.

Brad Larson
fuente
2
Gracias por la respuesta detallada. Tuve el mismo problema en el que definí un delegado bajo _unsafe_unretained y bloqueé mi aplicación, luego la arreglé cambiando a fuerte pero ahora tiene una pérdida de memoria. Entonces, lo cambié a débil y funciona de maravilla.
chathuram
@ichathura ¡Guau! Me salvaste del fango ARC. He encontrado el mismo bloqueo al usar CMPopTipView.
Nianliang
@BradLarson: "no tiene los problemas de detención o los perfiles de memoria de diente de sierra experimentados en las plataformas de recolección de basura". Esperaría peores perfiles de detención y memoria de diente de sierra de la recuperación basada en el alcance y un rendimiento mucho peor del recuento de referencias, por lo que me gustaría ver una comparación real.
Jon Harrop
Brad, el enlace de Chris Lattner está muerto . No estoy al 100% pero encontré este otro enlace. Lo que creo que es lo que querías vincular: lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160208/…
Miel
1
@Honey - Gracias por señalar eso. El que enlaza es ligeramente diferente, pero reemplacé el enlace muerto con una versión archivada del mensaje original. Está en los archivos de la lista de correo, que deberían estar disponibles en algún lugar, pero buscaré si puedo encontrar su nueva ubicación.
Brad Larson
14

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

Se puede engañar a ARC performSelector:si el compilador no puede descubrir cuál es el selector (el compilador generará una advertencia al respecto).

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

Rayas
fuente
7

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

  1. No invalidar NSTimers al descartar controladores de vista
  2. Olvidar eliminar cualquier observador a NSNotificationCenter cuando descarta el controlador de vista.
  3. Mantener fuertes referencias a uno mismo en bloques.
  4. Uso de referencias fuertes a delegados 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- probably- culprits/

Ed-E G
fuente
0

ARC tampoco gestionará los tipos de CoreFoundation. Puede 'puentearlos' (usando CFBridgingRelease()) pero solo si lo va a usar como un objeto Objective-C / Cocoa. Tenga en cuenta que CFBridgingRelease solo disminuye el conteo de retención de CoreFoundation en 1 y lo mueve al ARC de Objective-C.

MaddTheSane
fuente