Pregunta

Recién estoy empezando a echar un vistazo a Objective-C y Cocoa con vistas a jugar con el SDK de iPhone.Me siento razonablemente cómodo con las C malloc y free concepto, 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 lo he superado.

Como hacer release, retain y autorelease ¿Funciona y cuáles son las convenciones sobre su uso?

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

¿Fue útil?

Solución

Empecemos con retain y release; autorelease En realidad, es solo un caso especial una vez que se comprenden los conceptos básicos.

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

La forma básica en que esto difiere de malloc y free es que cualquier objeto determinado no necesita preocuparse de que otras partes del sistema fallen porque ha liberado la memoria que estaban usando.Suponiendo que todos sigan el juego y retengan/liberan de acuerdo con las reglas, cuando una parte del código retiene y luego libera el objeto, cualquier otra parte del código que también haga referencia al objeto no se verá afectada.

Lo que a veces puede resultar confuso es saber las circunstancias bajo las cuales debes llamar retain y release.Mi regla general es que si quiero conservar un objeto durante un período de tiempo (si es una variable miembro de 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 retain.Por convención, también se incrementa (en realidad se establece en 1) cuando el objeto se crea con un método "init".En cualquiera de estos casos, es mi responsabilidad llamar release en el objeto cuando termine con él.Si no lo hago, habrá una pérdida 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.La liberación automática se utiliza como una forma conveniente (y a veces necesaria) de indicarle al sistema que libere este objeto después de un tiempo.Desde una perspectiva de plomería, cuando autorelease se llama, el hilo actual NSAutoreleasePool es alertado de la llamada.El NSAutoreleasePool ahora sabe que una vez que tenga una oportunidad (después de la iteración actual del bucle de eventos), puede llamar release sobre el objeto.Desde nuestra perspectiva como programadores, se encarga de llamar release para nosotros, por lo que no tenemos que hacerlo (y de hecho, no deberíamos).

Lo que es importante tener en cuenta es que (nuevamente, por convención) toda creación de objetos clase Los métodos devuelven un objeto liberado automáticamente.Por ejemplo, en el siguiente ejemplo, la variable "s" tiene un recuento de referencia de 1, pero una vez que se completa el ciclo de eventos, se destruirá.

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

Si quieres aferrarte a esa cuerda, necesitarás llamar retain explícitamente y luego explícitamente release cuando hayas terminado.

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

- (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; sin embargo, en algún momento, hará clic.Aquí hay algunas referencias para ayudarlo a comenzar:

  • La introducción de Apple a la gestión de la memoria.
  • Programación Cocoa para Mac OS X (4.a edición), de Aaron Hillegas: un libro muy bien escrito con muchos ejemplos excelentes.Se lee como un tutorial.
  • Si realmente estás sumergiéndote, puedes dirigirte a Gran Rancho Nerd.Se trata de un centro de formación 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 excelente manera de aprender.

Otros consejos

Si comprende el proceso de retención/liberación, entonces hay dos reglas de oro que son "duh" obvias para los programadores establecidos de Cocoa, 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.debes llamar [object release] cuando hayas terminado con ello.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.debes llamar [object retain] si desea conservar el objeto después del final de su función.

Le vendría bien seguir también esta convención en las funciones que cree usted mismo.

(Quisquillosos:Sí, lamentablemente hay algunas llamadas API que son excepciones a estas reglas, pero son poco comunes).

Si está escribiendo código para el escritorio y puede apuntar a Mac OS X 10.5, al menos debería considerar el uso de la recolección de basura Objective-C.Realmente simplificará la mayor parte de su desarrollo; es por eso que Apple puso todo el esfuerzo en crearlo en primer lugar y en lograr que funcione bien.

En cuanto a las reglas de administración de memoria cuando no se usa GC:

  • Si crea un nuevo objeto usando +alloc/+allocWithZone:, +new, -copy o -mutableCopy o si tu -retain un objeto, usted está tomando posesión de él y debe asegurarse de que se envíe -release.
  • Si recibe un objeto de cualquier otra manera, está no el dueño del mismo y debe no asegúrese de que se envíe -release.
  • Si desea asegurarse de que se envíe un objeto -release Puedes enviarlo tú mismo o puedes enviar el objeto. -autorelease y la corriente grupo de liberación automática lo enviaré -release (una vez por recibido -autorelease) cuando se drena la piscina.

Típicamente -autorelease se utiliza como una forma de garantizar que los objetos vivan durante la duración del evento actual, pero se limpien después, ya que hay un grupo de liberación automática que rodea el procesamiento de eventos de Cocoa.En cacao, es lejos Es más común devolver objetos a una persona que llama que se liberan automáticamente que devolver objetos que la persona que llama necesita liberar.

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

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

Para algunos idiomas, las referencias aumentan y disminuyen automáticamente, pero Objective-c no es uno de esos idiomas.Por 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 acordarse de liberar los recursos adquiridos dentro del código es tedioso y propenso a errores.Objective-C introduce otro concepto destinado a hacer esto mucho más fácil:Grupos de liberación automática.Los grupos de liberación automática son objetos especiales que se instalan en cada subproceso.Son una clase bastante simple, si busca NSAutoreleasePool.

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

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

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

Debido a que el objeto se libera automáticamente, ya no necesitamos llamarlo explícitamente "liberar".Esto se debe a que sabemos que algún grupo de liberación automática lo hará por nosotros más adelante.

Ojalá esto ayude.El artículo de Wikipedia es bastante bueno sobre el recuento de referencias.Más información sobre Los grupos de liberación automática se pueden encontrar aquí..También tenga en cuenta que si está compilando para Mac OS X 10.5 y posterior, puede indicarle a Xcode que compile con la recolección de basura habilitada, lo que le permitirá ignorar por completo la retención/liberación/liberación automática.

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

Además, si está escribiendo una biblioteca o algo que podría reutilizarse, el uso del modo GC bloquea a cualquiera que use el código para que también use el modo GC, por lo que, según tengo entendido, cualquiera que intente escribir código ampliamente reutilizable tiende a optar por la administración. memoria manualmente.

Como siempre, cuando la gente empieza a intentar reformular el material de referencia, casi invariablemente se equivocan o proporcionan una descripción incompleta.

Apple proporciona una descripción completa del sistema de gestión de memoria de Cocoa en Guía de programación de gestión de memoria para Cocoa, al final del cual hay un breve pero preciso resumen de la Reglas de gestión de memoria.

No agregaré nada específico sobre retención/liberación, salvo que usted quiera pensar en gastar $50 y obtener el libro de Hillegass, pero le sugiero encarecidamente que comience a usar las herramientas de Instruments desde muy temprano en el desarrollo de su aplicación (incluso su ¡el primero!).Para hacerlo, Ejecutar->Iniciar con herramientas de rendimiento.Comenzaría con Leaks, que es solo uno de los muchos instrumentos disponibles, pero que le ayudará a mostrarle cuándo se le olvidó publicar.Es bastante desalentador la cantidad de información que se le presentará.Pero consulte este tutorial para ponerse en marcha rápidamente:
TUTORIAL DEL CACAO:REPARAR FUGAS DE MEMORIA CON INSTRUMENTOS

En realidad tratando de fuerza ¡Las fugas podrían ser una mejor manera de, a su vez, aprender cómo prevenirlas!Buena suerte ;)

Matt Dillard escribió:

return [[s liberación automática] liberación];

La liberación automática lo hace no conservar el objeto.La liberación automática simplemente lo pone en cola para ser liberado más tarde.No querrás tener una declaración de liberación allí.

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

gestión de la memoria del cacao

Hay un screencast gratuito disponible en iDeveloperTV Network.

Gestión de memoria en Objective-C

La respuesta de NilObject es un buen comienzo.Aquí hay información complementaria relacionada con la administración manual de la memoria (requerido en el iPhone).

Si usted personalmente alloc/init un objeto, viene con un recuento de referencia de 1.Usted es responsable de limpiarlo cuando ya no sea necesario, ya sea llamando [foo release] o [foo autorelease].release lo limpia de inmediato, mientras que autorelease agrega el objeto al grupo de liberación automática, que lo liberará automáticamente más adelante.

La liberación automática es principalmente para cuando tienes un método que necesita devolver el objeto en cuestión (por lo que no puedes liberarlo manualmente, de lo contrario devolverás un objeto nulo) pero tampoco querrás conservarlo.

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

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

pero desea conservar este objeto, debe llamar a [foo retener].De lo contrario, es posible que obtenga autoreleased y te aferrarás a una referencia nula (como lo haría en el anterior stringWithString ejemplo).Cuando ya no lo necesites, llama [foo release].

Las respuestas anteriores dan reformulaciones claras de lo que dice la documentación;El problema con el que se topa la mayoría de la gente nueva son los casos de indocumentados.Por ejemplo:

  • Liberación automática:Docs dice que activará un lanzamiento "en algún momento en el futuro". ¡¿CUANDO?!Básicamente, puede contar con que el objeto estará presente hasta que salga de su código y vuelva al bucle de eventos del sistema.El sistema PUEDE liberar el objeto en cualquier momento después del ciclo de evento actual.(Creo que Matt dijo eso antes).

  • cuerdas estáticas: NSString *foo = @"bar"; ¿Tienes que retenerlo o liberarlo?No.Qué tal si

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

    ...

    NSString *foo = [self getBar]; // still no need to retain or release
    
  • La regla de la creación:Si lo creó, es de su propiedad y se espera que lo publique.

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

Aquí hay un fragmento de Reglas muy simples para la gestión de la memoria en Cocoa:

Reglas de recuento de retención

  • Dentro de un bloque determinado, el uso de -copy, -alloc y -retain debe ser igual al uso de -release y -autorelease.
  • Objetos creados utilizando constructores de conveniencia (p. ej.stringWithString de NSString) se consideran de liberación automática.
  • Implemente un método -dealloc para liberar las variables de instancia que posee

La primera viñeta dice:si llamaste alloc (o new fooCopy), debe llamar a la liberación de ese objeto.

La segunda viñeta dice:si usas un constructor de conveniencia y necesitas el objeto para andar por ahí (como ocurre con una imagen que se dibujará más adelante), es necesario conservarla (y luego liberarla).

El tercero debería explicarse por sí mismo.

También hay mucha buena información sobre cocoadev:

Como ya mencionaron varias personas, Apple Introducción a la gestión de la memoria es, con diferencia, el mejor lugar para empezar.

Un enlace útil que aún no he visto mencionado es Gestión práctica de la memoria.Lo encontrará en medio de los documentos de Apple si los lee, pero vale la pena vincularlo directamente.Es un brillante resumen ejecutivo de las reglas de administración de la memoria con ejemplos y errores comunes (básicamente lo que otras respuestas aquí intentan explicar, pero no tan bien).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top