Comprensión del conteo de referencia con Cacao y Objective-C


Estoy empezando a echar un vistazo a Objective-C y Cocoa con el fin de jugar con el SDK de iPhone. Estoy razonablemente cómodo con el concepto de C malloc y free, pero el esquema de conteo de referencias de Cocoa me tiene bastante confundido. Me han dicho que es muy elegante una vez que lo entiendes, pero aún no he superado la joroba.

¿Cómo release, retain y autorelease trabajo y cuáles son las convenciones sobre su uso?

(O en su defecto, ¿qué leíste que te ayudó a conseguirlo?)

Author: Peter Mortensen, 2008-08-09

14 answers

Comencemos con retain y release; autorelease es realmente solo un caso especial una vez que entiendes los conceptos básicos.

En Cocoa, cada objeto realiza un seguimiento de cuántas veces se le hace referencia (específicamente, la clase base NSObject implementa esto). Al llamar a retain en un objeto, le estás diciendo que quieres aumentar su número de referencia en uno. Al llamar a release, le dices al objeto que lo estás soltando, y su número de referencias se decrementa. Si, después de llamar release, el el recuento de referencias es ahora cero, entonces la memoria de ese objeto es liberada por el sistema.

La forma básica en que esto difiere de malloc y free es que cualquier objeto dado no necesita preocuparse por otras partes del sistema que se bloqueen porque ha liberado la memoria que estaban usando. Suponiendo que todos están jugando y reteniendo / liberando de acuerdo con las reglas, cuando una pieza de código retiene y luego libera el objeto, cualquier otra pieza de código que también haga referencia al objeto será no afectado.

Lo que a veces puede ser confuso es conocer las circunstancias bajo las cuales debe llamar retain y release. Mi regla general es que si quiero aferrarme a un objeto durante algún tiempo (si es una variable miembro en una clase, por ejemplo), entonces necesito asegurarme de que el recuento de referencias del objeto sepa sobre mí. Como se describió anteriormente, el recuento de referencias de un objeto se incrementa llamando a retain. Por convención, también se incrementa (establecido en 1, realmente) cuando el el objeto se crea con un método "init". En cualquiera de estos casos, es mi responsabilidad llamar release al objeto cuando haya terminado con él. Si no lo hago, habrá una fuga de memoria.

Ejemplo de creación de objetos:

NSString* s = [[NSString alloc] init];  // Ref count is 1
[s retain];                             // Ref count is 2 - silly
                                        //   to do this after init
[s release];                            // Ref count is back to 1
[s release];                            // Ref count is 0, object is freed

Ahora para autorelease. Autorelease se usa como una forma conveniente (y a veces necesaria) de decirle al sistema que libere este objeto después de un tiempo. Desde una perspectiva de fontanería, cuando se llama autorelease, el hilo actual NSAutoreleasePool es alertado de la llamada. El NSAutoreleasePool ahora sabe que una vez que recibe una oportunidad (después de la iteración actual del bucle de eventos), puede llamar a release en el objeto. Desde nuestra perspectiva como programadores, se encarga de llamar release para nosotros, por lo que no tenemos que (y de hecho, no deberíamos).

Lo que es importante tener en cuenta es que (de nuevo, por convención) todos los métodos de creación de objetos class devuelven un objeto autoreleased. Por ejemplo, en el siguiente ejemplo, la variable " s " tiene un recuento de referencia de 1, pero después de que se complete el bucle de eventos, se destruirá.

NSString* s = [NSString stringWithString:@"Hello World"];

Si quieres aferrarte a esa cadena, necesitas llamar a retain explícitamente, y luego explícitamente release cuando hayas terminado.

Considere el siguiente bit de código (muy artificial), y verá una situación en la que se requiere autorelease:

- (NSString*)createHelloWorldString
{
    NSString* s = [[NSString alloc] initWithString:@"Hello World"];

    // Now what?  We want to return s, but we've upped its reference count.
    // The caller shouldn't be responsible for releasing it, since we're the
    // ones that created it.  If we call release, however, the reference 
    // count will hit zero and bad memory will be returned to the caller.  
    // The answer is to call autorelease before returning the string.  By 
    // explicitly calling autorelease, we pass the responsibility for
    // releasing the string on to the thread's NSAutoreleasePool, which will
    // happen at some later time.  The consequence is that the returned string 
    // will still be valid for the caller of this function.
    return [s autorelease];
}

Me doy cuenta de que todo esto es un poco confuso - en algún momento, sin embargo, hará clic. Aquí hay algunas referencias para ponerte en marcha:

  • de Apple introducción a la gestión de memoria.
  • Cocoa Programming for Mac OS X (4th Edition) , por Aaron Hillegas - un libro muy bien escrito con muchos grandes ejemplos. Se lee como un tutorial.
  • Si realmente estás buceando, podrías dirigirte a Big Nerd Ranch. Este es un centro de entrenamiento dirigido por Aaron Hillegas, el autor del libro mencionado anteriormente. Asistí al curso de Introducción al Cacao allí hace varios años, y fue una gran manera de aprender.
 148
Author: Matt Dillard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-30 20:41:22

Si entiendes el proceso de retain/release, entonces hay dos reglas de oro que son "duh" obvias para los programadores de Cocoa establecidos, pero desafortunadamente rara vez se explican claramente para los recién llegados.

  1. Si una función que devuelve un objeto tiene alloc, create o copy en su nombre entonces el objeto es tuyo. Debe llamar [object release] cuando haya terminado con él. O CFRelease(object), si es un objeto Core-Foundation.

  2. Si NO tiene una de estas palabras en su nombre entonces el objeto pertenece a otra persona. Debe llamar a [object retain] si desea mantener el objeto después del final de su función.

Usted estaría bien servido para seguir también esta convención en las funciones que usted mismo crea.

(Nitpickers: Sí, desafortunadamente hay algunas llamadas API que son excepciones a estas reglas, pero son raras).

 10
Author: Andrew Grant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-19 03:41:31

Si está escribiendo código para el escritorio y puede apuntar a Mac OS X 10.5, al menos debe buscar el uso de Objective-C garbage collection. Realmente simplificará la mayor parte de su desarrollo - es por eso que Apple puso todo el esfuerzo en crearlo en primer lugar, y hacer que funcione bien.

En cuanto a las reglas de gestión de memoria cuando no se utiliza GC:

  • Si se crea un nuevo objeto usando +alloc/+allocWithZone:, +new, -copy o -mutableCopy o si -retain un objeto, usted está tomando propiedad de la misma y debe asegurarse de que se envía -release.
  • Si recibe un objeto de cualquier otra manera, usted es no el propietario del mismo y debe no asegurarse de que se envía -release.
  • Si desea asegurarse de que se envía un objeto -release puede enviarlo usted mismo, o puede enviar el objeto -autorelease y el actual autorelease pool lo enviará -release (una vez por recibido -autorelease) cuando se agote el grupo.

Típicamente -autorelease se usa como una forma de asegurar que los objetos vivan durante la duración del evento actual, pero se limpien después, ya que hay un grupo de autorelease que rodea el procesamiento de eventos de Cocoa. En Cocoa, es mucho más común devolver objetos a un llamador que son autoreleased que devolver objetos que el mismo llamador necesita liberar.

 8
Author: Chris Hanson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-09 22:27:12

Objective-C usa Recuento de referencia, lo que significa que cada Objeto tiene un recuento de referencia. Cuando se crea un objeto, tiene un recuento de referencia de "1". En pocas palabras, cuando se hace referencia a un objeto (es decir, almacenado en algún lugar), se "retiene", lo que significa que su recuento de referencias se incrementa en uno. Cuando un objeto ya no es necesario, se "libera", lo que significa que su recuento de referencia se reduce en uno.

Cuando el recuento de referencia de un objeto es 0, el objeto se libera. Esto es básico conteo de referencias.

Para algunos idiomas, las referencias se incrementan y disminuyen automáticamente, pero objective-c no es uno de esos idiomas. Por lo tanto, el programador es responsable de retener y liberar.

Una forma típica de escribir un método es:

id myVar = [someObject someMessage];
.... do something ....;
[myVar release];
return someValue;

El problema de tener que recordar liberar cualquier recurso adquirido dentro del código es tedioso y propenso a errores. Objective-C introduce otro concepto dirigido a hacer esto mucho más fácil: Autorelease Pools. Los pools de autorelease son objetos especiales que se instalan en cada hilo. Son una clase bastante simple, si buscas NSAutoreleasePool.

Cuando un objeto recibe un mensaje de "autorelease" enviado a él, el objeto buscará cualquier grupo de autorelease que se encuentre en la pila para este hilo actual. Agregará el objeto a la lista como un objeto para enviar un mensaje de "liberación" en algún momento en el futuro, que generalmente es cuando se libera el grupo en sí.

Tomando el código anterior, usted puede reescribirlo para que sea más corto y fácil de leer diciendo:

id myVar = [[someObject someMessage] autorelease];
... do something ...;
return someValue;

Debido a que el objeto es autoreleased, ya no necesitamos llamar explícitamente a "release" en él. Esto se debe a que sabemos que algún grupo de autorelease lo hará por nosotros más tarde.

Esperemos que esto ayude. El artículo de Wikipedia es bastante bueno sobre el conteo de referencias. Puede encontrar más información sobre autorelease pools aquí. También tenga en cuenta que si está compilando para Mac OS X 10.5 y versiones posteriores, puede decirle a Xcode que compile con la recolección de basura habilitada, lo que le permite ignorar completamente retain/release/autorelease.

 6
Author: NilObject,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-09 03:48:10

Joshua (#6591) - El material de recolección de basura en Mac OS X 10.5 parece bastante genial, pero no está disponible para el iPhone (o si desea que su aplicación se ejecute en versiones anteriores a la 10.5 de Mac OS X).

Además, si estás escribiendo una biblioteca o algo que podría ser reutilizado, usando el modo GC bloquea a cualquiera que use el código también usando el modo GC, así que según lo entiendo, cualquiera que intente escribir código ampliamente reutilizable tiende a administrar la memoria manualmente.

 6
Author: Matt Sheppard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-09 03:59:35

Como siempre, cuando las personas comienzan a tratar de volver a escribir el material de referencia, casi invariablemente obtienen algo mal o proporcionan una descripción incompleta.

Apple proporciona una descripción completa del sistema de gestión de memoria de Cocoa en Memory Management Programming Guide for Cocoa, al final de la cual hay un breve pero preciso resumen de las Reglas de Gestión de Memoria .

 6
Author: mmalc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-20 00:42:12

No voy a añadir a la específica de retain / release aparte de que es posible que desee pensar en dejar caer $50 y obtener el libro Hillegass, pero me gustaría sugerir encarecidamente entrar en el uso de las herramientas de Instrumentos muy temprano en el desarrollo de su aplicación (incluso su primera!). Para ello, Ejecute - > Start with performance tools. Comenzaría con Fugas que es solo uno de muchos de los instrumentos disponibles, pero le ayudará a mostrar cuando se ha olvidado de liberar. Es desalentador dejar de cuánto información que se le presentará. Pero echa un vistazo a este tutorial para levantarse y ir rápido:
TUTORIAL DE COCOA: ARREGLAR FUGAS DE MEMORIA CON INSTRUMENTOS

En realidad tratar de forzar fugas podría ser una mejor manera de, a su vez, aprender cómo prevenirlas! Buena suerte ;)

 6
Author: Rob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-27 13:24:21

Matt Dillard escribió :

Return [[s autorelease] release];

Autorelease hace no retener el objeto. Autorelease simplemente lo pone en cola para ser lanzado más tarde. Usted no quiere tener una declaración de liberación allí.

 5
Author: NilObject,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:32:05

Mi colección habitual de artículos de gestión de memoria de cacao:

Gestión de memoria de cacao

 5
Author: NANNAV,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-18 11:32:49

Hay un screencast gratuito disponible en la red iDeveloperTV

Gestión de memoria en Objective-C

 4
Author: Abizern,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-17 06:54:52

La respuesta de NilObject es un buen comienzo. Aquí hay información adicional relativa a la gestión manual de memoria (requerido en el iPhone ).

Si usted personalmente alloc/init un objeto, viene con un recuento de referencia de 1. Usted es responsable de limpiar después de que ya no es necesario, ya sea llamando [foo release] o [foo autorelease]. release lo limpia de inmediato, mientras que autorelease agrega el objeto al grupo de autorelease, que lo liberará automáticamente en un momento posterior.

Autorelease es principalmente para cuando tienes un método que necesita devolver el objeto en cuestión ( así que no puedes liberarlo manualmente, de lo contrario devolverás un objeto nil) pero tampoco quieres aferrarte a él.

Si adquiere un objeto al que no llamó alloc/init para obtenerlo {por ejemplo:

foo = [NSString stringWithString:@"hello"];

Pero si desea aferrarse a este objeto, debe llamar a [foo retain]. De lo contrario, es posible que obtendrá autoreleased y usted se aferra a un referencia nula (como en el ejemplo anterior stringWithString ). Cuando ya no lo necesite, llame a [foo release].

 4
Author: Mike McMaster,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-25 21:13:23

Las respuestas anteriores dan claras reafirmaciones de lo que dice la documentación; el problema con el que se topan la mayoría de las personas nuevas son los casos indocumentados. Por ejemplo:

  • Autorelease : los documentos dicen que activará una versión "en algún momento en el futuro."¿CUÁNDO?! Básicamente, puede contar con que el objeto esté alrededor hasta que salga de su código de nuevo en el bucle de eventos del sistema. El sistema PUEDE liberar el objeto en cualquier momento después del ciclo de eventos actual. (Creo que Matt dijo que, antes.)

  • Cadenas estáticas: NSString *foo = @"bar"; -- ¿tienes que retener o liberar eso? No. ¿Qué tal

    -(void)getBar {
        return @"bar";
    }
    

    ...

    NSString *foo = [self getBar]; // still no need to retain or release
    
  • La Regla de Creación: Si la ha creado, es suya y se espera que la libere.

En general, la forma en que los nuevos programadores de Cocoa se confunden es al no entender qué rutinas devuelven un objeto con un retainCount > 0.

Aquí hay un fragmento de Reglas Muy Simples Para la Administración de Memoria En Cacao:

Reglas de recuento de retención

  • Dentro de un bloque dado, el uso de-copy, -alloc y-retain debe ser igual al uso de-release y-autorelease.
  • Los objetos creados usando constructores de conveniencia (por ejemplo, stringWithString de NSString) se consideran autoreleased.
  • Implementar un método-dealloc para liberar las instancevariables que posee

La primera bala dice: si llamaste alloc (o new fooCopy), necesitas para llamar a la liberación de ese objeto.

La segunda viñeta dice: si usas un constructor de conveniencia y necesitas que el objeto se cuelgue (como con una imagen que se dibujará más tarde), necesitas retenerlo (y luego liberarlo).

El 3er debe explicarse por sí mismo.

 2
Author: Olie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-01 17:01:06

Mucha información sobre cocoadev también:

 1
Author: charles,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-15 20:46:23

Como ya han mencionado varias personas, la Introducción de Apple a la Gestión de la Memoria es, con mucho, el mejor lugar para comenzar.

Un enlace útil que aún no he visto mencionado es Gestión Práctica de la Memoria. Lo encontrarás en medio de los documentos de Apple si los lees, pero vale la pena hacer enlaces directos. Es un brillante resumen ejecutivo de las reglas de gestión de la memoria con ejemplos y errores comunes (básicamente lo que otras respuestas aquí están tratando de explicar, pero no como bien).

 0
Author: Brian Moeskau,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-06 03:01:11