objective framework development awesome apple app iphone objective-c cocoa memory

iphone - framework - swift ios documentation



Entendiendo el conteo de referencias con Cocoa y Objective-C (14)

Matt Dillard escribió :

retorno [[s autorelease] release];

Autorelease no retiene el objeto. Autorelease simplemente lo pone en cola para ser lanzado más tarde. No desea tener una declaración de liberación allí.

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

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

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



Como ya mencionaron varias personas, la developer.apple.com/mac/library/documentation/Cocoa/Conceptual/… Apple es, con mucho, el mejor lugar para comenzar.

Un enlace útil que no he visto mencionado todavía es developer.apple.com/mac/library/documentation/Cocoa/Conceptual/… . Lo encontrará en medio de los documentos de Apple si los lee, pero vale la pena hacer un enlace directo. Es un resumen ejecutivo brillante de las reglas de administración de memoria con ejemplos y errores comunes (básicamente, lo que intentan explicar otras respuestas aquí, pero no también).



Joshua (# 6591) - El material de recolección de basura en Mac OS X 10.5 parece bastante bueno, pero no está disponible para el iPhone (o si quieres que tu aplicación se ejecute en versiones anteriores a la 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 y también el modo GC, por lo que entiendo, cualquier persona que intente escribir un código ampliamente reutilizable tiende a administrar Memoria manualmente.


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

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

autorelease es principalmente para cuando tiene un método que necesita devolver el objeto en cuestión ( por lo que no puede liberarlo manualmente, de lo contrario, estará devolviendo un objeto nulo ) pero tampoco desea mantenerlo. .

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

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

pero desea aferrarse a este objeto, debe llamar a [foo retain]. De lo contrario, es posible que se autoreleased y mantendrá una referencia nula (como lo haría en el ejemplo anterior stringWithString ). Cuando ya no lo necesite, llame a [foo release] .


Las respuestas anteriores dan una explicación clara de lo que dice la documentación; El problema con el que se topa la mayoría de las personas nuevas es en los casos no documentados. Por ejemplo:

  • Autorelease : los documentos dicen que activará un lanzamiento "en algún momento en el futuro". ¡¿CUANDO?! Básicamente, puede contar con que el objeto esté alrededor hasta que salga de su código nuevamente dentro del ciclo 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).

  • 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 creaste, la posees y se espera que la libere.

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

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

Reglas de conteo de retención

  • Dentro de un bloque dado, el uso de -copia, -alloc y -retain debe ser igual al uso de -release y -autorelease.
  • Los objetos creados utilizando constructores de conveniencia (por ejemplo, stringWithString de NSString) se consideran lanzados automáticamente.
  • Implemente un método -dealloc para liberar las variables de instancia que posee

La primera viñeta dice: si llamó a alloc (o new fooCopy ), debe llamar a release en ese objeto.

La segunda viñeta dice: si utiliza un constructor de conveniencia y necesita que el objeto se cuelgue (como con una imagen que se dibujará más adelante), debe conservarlo (y luego liberarlo).

El tercero debe ser autoexplicativo.




No agregaré más información específica sobre retener / lanzar, de lo que quizás desee pensar en retirar $ 50 y obtener el libro Hillegass, pero le sugeriría encarecidamente que use las herramientas de Instrumentos al inicio del desarrollo de su aplicación (incluso su ¡el primero!). Para hacerlo, Ejecute-> Comience con las herramientas de rendimiento. Comenzaría con Leaks, que es solo uno de los muchos instrumentos disponibles, pero te ayudará a mostrarte cuándo has olvidado lanzar. Se ha dejado intimidado la cantidad de información que se le presentará. Pero echa un vistazo a este tutorial para levantarse e ir rápido:
TUTORIAL DE COCOA: LA FIJACIÓN DE LA MEMORIA FUGA CON LOS INSTRUMENTOS

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


Objective-C usa el conteo de referencias , lo que significa que cada objeto tiene un conteo 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, se almacena en algún lugar), se "retiene", lo que significa que su cuenta de referencia 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 referencias de un objeto es 0, el objeto se libera. Este es el conteo básico de referencia.

Para algunos idiomas, las referencias se incrementan y disminuyen automáticamente, pero object-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 la necesidad de recordar liberar todos 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: Autorelease Pools. Los grupos de autorelease son objetos especiales que se instalan en cada hilo. Son una clase bastante simple, si buscas en NSAutoreleasePool.

Cuando un objeto recibe un mensaje de "autorelease", 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 ocurre cuando se libera el grupo.

Tomando el código anterior, 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 se lanza automáticamente, ya no necesitamos llamar explícitamente "release" en él. Esto se debe a que sabemos que un grupo de autorelease lo hará por nosotros más adelante.

Esperemos que esto ayude. El artículo de Wikipedia es bastante bueno sobre el recuento de referencias. Más información sobre los grupos de autorelease se puede encontrar 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 retener / liberar / autorelease.


Si entiendes el proceso de retener / liberar, hay dos reglas de oro que son "duh" obvias para los programadores establecidos de Cocoa, pero desafortunadamente rara vez se explican con claridad 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 suyo. Debes llamar [object release] cuando hayas terminado. 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.

Estaría bien preparado para seguir también esta convención en las funciones que cree usted mismo.

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


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 su esfuerzo en crearlo en primer lugar y hacerlo funcionar bien.

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

  • Si crea un nuevo objeto utilizando +alloc/+allocWithZone: +new , -mutableCopy o -mutableCopy o si -retain un objeto, usted es el propietario del mismo y debe asegurarse de que se envíe -release .
  • Si recibe un objeto de alguna otra manera, no es su propietario y no debe asegurarse de que se envíe.
  • Si desea asegurarse de que se envía un objeto, por -release , puede enviarlo usted mismo, o puede enviar el objeto -autorelease y el grupo de autorelease actual lo enviará -release (una vez por cada -autorelease recibido) cuando se -autorelease el grupo.

Por -autorelease general, el modo de -autorelease se utiliza para garantizar que los objetos vivan durante la duración del evento actual, pero luego se limpian, ya que existe un grupo de autorelease que rodea el procesamiento de eventos de Cocoa. En Cocoa, es mucho 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.


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

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

La forma básica en que esto difiere de malloc y de forma free es que cualquier objeto dado no tiene que preocuparse de que otras partes del sistema se bloqueen porque ha liberado la memoria que estaban usando. Asumiendo 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 no se verá afectada.

Lo que a veces puede ser confuso es saber en qué circunstancias debe llamar retain y release . Mi regla general es que si quiero aferrarme a un objeto durante un período de tiempo (si es una variable miembro en una clase, por ejemplo), debo asegurarme de que el recuento de referencias del objeto sepa de mí. Como se describió anteriormente, el recuento de referencia de un objeto se incrementa al llamar a retain . Por convención, también se incrementa (se establece en 1, realmente) cuando el objeto se crea con un método "init". En cualquiera de estos casos, es mi responsabilidad llamar release sobre el objeto cuando termine. 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 . Autorelease se usa como una forma conveniente (y, a veces, necesaria) de decirle al sistema que libere este objeto poco después. Desde la perspectiva de la plomería, cuando se llama la NSAutoreleasePool se avisa de la llamada a NSAutoreleasePool del hilo actual. NSAutoreleasePool ahora sabe que una vez que obtiene una oportunidad (después de la iteración actual del bucle de eventos), puede invocar la release en el objeto. Desde nuestra perspectiva como programadores, nos encargamos de llamar a release , así que no tenemos que hacerlo (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 clase de creación de objetos devuelven un objeto lanzado automáticamente. 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 desea aferrarse a esa cadena, tendrá que llamar a retain explícitamente y luego release explícitamente cuando haya terminado.

Considere el siguiente bit de código (muy artificial), y verá una situación en la que se requiere la 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:

  • La introducción de Apple a la gestión de memoria.
  • Programación de cacao para Mac OS X (4ª edición) , de Aaron Hillegas - un libro muy bien escrito con muchos grandes ejemplos. Se lee como un tutorial.
  • Si realmente estás buceando, puedes 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 hace varios años, y fue una excelente manera de aprender.