objective-c cocoa reference-counting retaincount

objective c - Llamar a-retainCount considerado dañino



objective-c cocoa (2)

O, por qué no retainCount en mis vacaciones de verano

Esta publicación tiene como objetivo solicitar informes detallados sobre los por qué y las causas de ese infame método, retainCount , para consolidar la información relevante que flota en torno a SO. *

  1. Lo básico: ¿Cuáles son las razones oficiales para no usar retainCount ? ¿Hay alguna situación en la que pueda ser útil? ¿Qué se debe hacer en su lugar? ** Siéntase libre de editorializar.

  2. Histórico / explicativo: ¿Por qué Apple proporciona este método en el protocolo NSObject si no está destinado a ser utilizado? ¿El código de Apple se basa en retainCount para algún propósito? Si es así, ¿por qué no está escondido en alguna parte?

  3. Para una comprensión más profunda: ¿Cuáles son las razones por las que un objeto puede tener un recuento de retención diferente del que se supondría a partir del código de usuario? ¿Puede dar ejemplos *** de los procedimientos estándar que podría usar el código marco que causan esa diferencia? ¿Hay algún caso conocido en el que el conteo retenido sea siempre diferente de lo que un nuevo usuario podría esperar?

  4. ¿Algo más que piense que vale la pena mencionar sobre retainCount ?

* Los codificadores que son nuevos en Objective-C y Cocoa a menudo lidian, o al menos malinterpretan, el esquema de recuento de referencias. Las explicaciones de los tutoriales pueden mencionar conteos de retención, que (de acuerdo con estas explicaciones) aumentan en uno al llamar retain , alloc , copy , etc., y uno en uno cuando se release (y en algún momento en el futuro cuando se llama autorelease )

Un pirata informático en ciernes, Kris, podría así tener la idea de que verificar el conteo de retainCount un objeto sería útil para resolver algunos problemas de memoria, y he aquí, ¡hay un método disponible en cada objeto llamado retainCount ! Kris llama a retainCount a retainCount en un par de objetos, y este es demasiado alto, y ese es demasiado bajo, ¿y qué diablos está pasando? Entonces Kris hace una publicación en SO, "¿Qué pasa con mi gestión de memoria?" y luego un enjambre de letras <bold>, <large> desciende diciendo "¡No hagas eso! No puedes confiar en los resultados", lo cual está muy bien, pero nuestro codificador intrépido puede querer una explicación más profunda.

Espero que esto se convierta en preguntas frecuentes, una página de buenos ensayos informativos / conferencias de cualquiera de nuestros expertos que estén dispuestos a escribir una, que los nuevos Cocoa-heads se puedan señalar cuando se preguntan acerca de retainCount .

** No quiero que esto sea demasiado amplio, pero los consejos específicos de la experiencia o los documentos sobre la verificación / depuración de retener y liberar los emparejamientos pueden ser apropiados aquí.

*** En código simulado; obviamente, el público en general no tiene acceso al código real de Apple.


Lo básico: ¿Cuáles son las razones oficiales para no usar retainCount?

La administración de Autorelease es la más obvia: no tiene forma de estar seguro de cuántas de las referencias representadas por retainCount encuentran en un grupo de retainCount local o externo (en un subproceso secundario o en el grupo local de otro subproceso).

Además, algunas personas tienen problemas con fugas, y en un recuento de referencias de nivel superior y cómo los grupos de autorelease funcionan en niveles fundamentales. Escribirán un programa sin (mucho) respeto al recuento de referencias adecuado, o sin aprender a contar correctamente. Esto hace que su programa sea muy difícil de depurar, probar y mejorar; también es una rectificación que consume mucho tiempo.

La razón para desalentar su uso (a nivel del cliente) es doble:

1) El valor puede variar por muchas razones. Enhebrar solo es razón suficiente para nunca confiar en él.

2) Aún tiene que implementar el recuento correcto de referencias. retainCount nunca te salvará del recuento de referencias desequilibrado.

¿Hay alguna situación en la que pueda ser útil?

De hecho, podría usarlo de manera significativa si escribió sus propios asignadores o esquema de recuento de referencias, o si su objeto vivía en un hilo y tenía acceso a todos los grupos de autorreleases en los que podría existir. Esto también implica que no lo haría compártelo con cualquier API externa. La manera fácil de simular esto es crear un programa con un hilo, cero conjuntos de autorreleases, y hacer su referencia contando la forma ''normal''. Es poco probable que alguna vez necesite resolver este problema / escribir este programa para otra cosa que no sean razones "académicas".

Como un auxiliar de depuración: puede usarlo para verificar que el recuento de retención no sea inusualmente alto. Si toma este enfoque, tenga en cuenta las variaciones de implementación (algunas se citan en esta publicación) y no confíe en ellas. Ni siquiera ejecute las pruebas en su repositorio de SCM.

Esto puede ser un diagnóstico útil en circunstancias extremadamente raras. Se puede usar para detectar:

  • Retención excesiva: una asignación con un desequilibrio positivo en el recuento retenido no se mostraría como una pérdida si su programa puede alcanzar la asignación.

  • Un objeto al que hacen referencia muchos otros objetos: una ilustración de este problema es un recurso compartido (mutable) o colección que opera en un contexto multiproceso. El acceso frecuente o los cambios a este recurso / colección pueden introducir un cuello de botella significativo en la ejecución de su programa.

  • Niveles Autorelease: Autoreleasing, grupos de liberación automática y ciclos de retención / liberación automática tienen un costo. Si necesita minimizar o reducir el uso y / o crecimiento de la memoria, puede usar este enfoque para detectar casos excesivos.

De los comentarios con Bavarious (abajo): un valor alto también puede indicar una asignación invalidada (instancia dealloc''d). Esto es completamente un detalle de implementación, y nuevamente, no utilizable en el código de producción. Enviar esta asignación a mensajes provocaría un error cuando los zombies estén habilitados.

¿Qué debería hacerse en su lugar?

Si no eres responsable de devolver la memoria a ti self (es decir, no has escrito un asignador), déjala en paz; es inútil.

Tienes que aprender el recuento de referencias adecuado.

Para comprender mejor el uso de liberación y liberación automática, configure algunos puntos de interrupción y comprenda cómo se usan, en qué casos, etc. Aún tendrá que aprender a usar el recuento de referencias correctamente, pero esto puede ayudarlo a comprender por qué es inútil. .

Incluso más simple: utilice instrumentos para rastrear los recuentos y recuentos de ref, luego analice el recuento de ref y las cantidades de llamadas de varios objetos en un programa activo.

Histórico / explicativo: ¿Por qué Apple proporciona este método en el protocolo NSObject si no está destinado a ser utilizado? ¿El código de Apple se basa en retainCount para algún propósito? Si es así, ¿por qué no está escondido en alguna parte?

Podemos suponer que es público por dos razones principales:

1) Recuento de referencias adecuado en entornos gestionados. Está bien que los asignadores usen retainCount - realmente. Es un concepto muy simple. Cuando se -[NSObject release] se puede -[NSObject release] el contador de ref (a menos que se anule) y el objeto puede desasignarse si retainCount es 0 (después de llamar a dealloc). Todo esto está bien en el nivel de asignador. Los asignadores y las zonas (en gran parte) se abstraen, por lo que ... esto hace que el resultado no tenga sentido para los clientes comunes. Consulte el comentario con bbum (abajo) para obtener detalles sobre por qué retainCount no puede ser igual a 0 en el nivel de cliente, desasignación de objeto, secuencias de desasignación y más.

2) Para ponerlo a disposición de los subclasistas que desean un comportamiento personalizado y porque los demás métodos de recuento de referencias son públicos. Puede ser útil en algunos casos, pero normalmente se usa por las razones equivocadas (por ej., Singletons inmortales). Si necesita su propio esquema de conteo de referencias, entonces esta familia puede valer la pena.

Para una comprensión más profunda: ¿Cuáles son las razones por las que un objeto puede tener un recuento de retención diferente del que se supondría a partir del código de usuario? ¿Puede dar ejemplos *** de los procedimientos estándar que podría usar el código marco que causan esa diferencia? ¿Hay algún caso conocido en el que el conteo retenido sea siempre diferente de lo que un nuevo usuario podría esperar?

De nuevo, un esquema de conteo de referencia personalizado y objetos inmortales. NSCFString literales NSCFString entran en la última categoría:

NSLog(@"%qu", [@"MyString" retainCount]); // Logs: 1152921504606846975

¿Algo más que piense que vale la pena mencionar sobre retainCount?

Es inútil como una ayuda de depuración. Aprenda a usar análisis de fugas y zombies, y úselos a menudo, incluso después de que tenga un control sobre el recuento de referencias.

Actualización: bbum ha publicado recientemente un artículo titulado RetenerCount es inútil . El artículo contiene una discusión exhaustiva de por qué -retainCount no es útil en la gran mayoría de los casos.


La regla general es que si estás utilizando este método, es mejor que estés seguro de que sabes lo que estás haciendo. Si lo está utilizando para depurar una fuga de memoria lo está haciendo mal, si lo está haciendo para ver qué está pasando con un objeto, lo está haciendo mal.

Hay un caso en el que lo he usado y lo encontré útil. Eso es al hacer un caché de objetos compartido donde quería vaciar el objeto cuando ya nada tenía referencia. En esta situación esperé hasta que el valor de retención sea igual a 1, y luego puedo liberarlo sabiendo que no hay nada más reteniéndolo, obviamente esto no funcionará correctamente en los entornos recolectados y hay mejores formas de hacerlo. Pero este sigue siendo el único caso de uso "válido" que he visto para él, y no es algo que mucha gente vaya a hacer.