En su mayor parte con ARC (conteo automático de referencia), no necesitamos pensar en la administración de memoria en absoluto con los objetos Objective-C. Ya no está permitido crear NSAutoreleasePool
s, sin embargo, hay una nueva sintaxis:
@autoreleasepool {
…
}
Mi pregunta es, ¿por qué necesitaría esto cuando se supone que no debo lanzar / liberar automáticamente?
EDITAR: Para resumir lo que obtuve de todas las respuestas y comentarios sucintamente:
Nueva sintaxis:
@autoreleasepool { … }
es nueva sintaxis para
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
…
[pool drain];
Más importante:
- ARC utiliza
autorelease
tan bien comorelease
. - Necesita un grupo de liberación automática para hacerlo.
- ARC no crea el grupo de lanzamiento automático para usted. Sin embargo:
- El hilo principal de cada aplicación de Cocoa ya tiene un grupo de liberación automática.
- Hay dos ocasiones en las que es posible que desee utilizar
@autoreleasepool
:- Cuando se encuentra en un subproceso secundario y no hay un grupo de liberación automática, debe hacer el suyo para evitar fugas, como
myRunLoop(…) { @autoreleasepool { … } return success; }
. - Cuando desee crear un grupo más local, como ha mostrado @mattjgalloway en su respuesta.
- Cuando se encuentra en un subproceso secundario y no hay un grupo de liberación automática, debe hacer el suyo para evitar fugas, como
Respuestas:
ARC no elimina las retenciones, los lanzamientos y los lanzamientos automáticos, solo agrega los necesarios para usted. Así que todavía hay llamadas para retener, todavía hay llamadas para liberar, todavía hay llamadas para liberar automáticamente y todavía hay grupos de liberación automática.
Uno de los otros cambios que hicieron con el nuevo compilador Clang 3.0 y ARC es que reemplazaron
NSAutoReleasePool
con la@autoreleasepool
directiva del compilador.NSAutoReleasePool
siempre fue un poco un "objeto" especial y lo lograron para que la sintaxis de usar uno no se confunda con un objeto, por lo que generalmente es un poco más simple.Básicamente, lo necesitas
@autoreleasepool
porque todavía hay grupos de liberación automática de los que preocuparte. Simplemente no necesita preocuparse por agregarautorelease
llamadas.Un ejemplo de uso de un grupo de liberación automática:
Un ejemplo enormemente ideado, claro, pero si no tuvieras el
@autoreleasepool
interior delfor
bucle externo , estarías liberando 100000000 objetos más tarde que 10000 cada vez alrededor delfor
bucle externo .Actualización: Consulte también esta respuesta: https://stackoverflow.com/a/7950636/1068248 , para
@autoreleasepool
saber por qué no tiene nada que ver con ARC.Actualización: eché un vistazo a lo interno de lo que está sucediendo aquí y lo escribí en mi blog . Si echas un vistazo allí, verás exactamente qué está haciendo ARC y cómo
@autoreleasepool
el compilador utiliza el nuevo estilo y cómo introduce un alcance para inferir información sobre qué retenciones, lanzamientos y lanzamientos automáticos son necesarios.fuente
@autoreleasepool
para mí también? Si no estoy controlando lo que se libera o libera automáticamente (ARC lo hace por mí), ¿cómo debo saber cuándo configurar un grupo de liberación automática?@autoreleasepool
no libera automáticamente nada. Crea un grupo de liberación automática, de modo que cuando se alcanza el final del bloque, cualquier objeto que haya sido liberado automáticamente por ARC mientras el bloque estaba activo recibirá mensajes de liberación. La Guía de programación de administración de memoria avanzada de Apple lo explica así:fuente
release
mensaje, pero si el recuento de retención es> 1, el objeto NO será desasignado.Las personas a menudo malinterpretan ARC para algún tipo de recolección de basura o similar. La verdad es que, después de un tiempo, la gente de Apple (gracias a los proyectos de llvm y clang) se dio cuenta de que la administración de memoria de Objective-C (todo
retains
yreleases
, etc.) puede automatizarse completamente en tiempo de compilación . Esto es, solo leyendo el código, ¡incluso antes de que se ejecute! :)Para hacerlo, solo hay una condición: DEBEMOS seguir las reglas ; de lo contrario, el compilador no podría automatizar el proceso en el momento de la compilación. Por lo tanto, para asegurar que nunca se romper las reglas, no se nos permite escribir de forma explícita
release
,retain
etc. Esas llamadas se inyectan automáticamente en nuestro código por el compilador. Por lo tanto internamente todavía tenemosautorelease
s,retain
,release
, etc. Es simplemente que no es necesario escribirlos más.La A de ARC es automática en tiempo de compilación, que es mucho mejor que en tiempo de ejecución como la recolección de basura.
Todavía lo tenemos
@autoreleasepool{...}
porque tenerlo no rompe ninguna de las reglas, somos libres de crear / drenar nuestro grupo cada vez que lo necesitemos :).fuente
Esto se debe a que aún debe proporcionar al compilador pistas sobre cuándo es seguro que los objetos liberados automáticamente salgan del alcance.
fuente
@autoreleasepool
Eso significa que ahora necesito agregar porque no sé si ARC podría decidir liberar automáticamente algo?Citado de https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html :
...
fuente
Se requieren agrupaciones de liberación automática para devolver objetos recién creados de un método. Por ejemplo, considere este fragmento de código:
La cadena creada en el método tendrá un recuento de retención de uno. Ahora, ¿quién equilibrará ese conteo con un lanzamiento?
El método en sí? No es posible, tiene que devolver el objeto creado, por lo que no debe liberarlo antes de devolverlo.
La persona que llama del método? La persona que llama no espera recuperar un objeto que necesita ser liberado, el nombre del método no implica que se cree un nuevo objeto, solo dice que se devuelve un objeto y este objeto devuelto puede ser uno nuevo que requiera una liberación, pero puede Bueno, sea uno existente que no lo hace. Lo que el método devuelve puede incluso depender de algún estado interno, por lo que la persona que llama no puede saber si tiene que liberar ese objeto y no debería importarle.
Si la persona que llama siempre tiene que liberar todos los objetos devueltos por convención, entonces todos los objetos que no se crearon siempre tendrían que retenerse antes de devolverlo de un método y la persona que llama tendría que liberarlos una vez que salga del alcance, a menos que Se devuelve de nuevo. Esto sería muy ineficiente en muchos casos, ya que se puede evitar por completo alterar los recuentos de retención en muchos casos si la persona que llama no siempre libera el objeto devuelto.
Es por eso que hay grupos de liberación automática, por lo que el primer método de hecho se convertirá
Llamando
autorelease
sobre un objeto lo añade a la piscina autorelease, pero ¿qué significa eso realmente, la adición de un objeto a la piscina autorelease? Bueno, significa decirle a su sistema " Quiero que libere ese objeto por mí, pero en algún momento posterior, no ahora; tiene un recuento de retención que necesita ser equilibrado por una liberación; de lo contrario, la memoria se perderá, pero no puedo hacerlo yo mismo en este momento, ya que necesito que el objeto permanezca vivo más allá de mi alcance actual y mi interlocutor tampoco lo hará por mí, no tiene conocimiento de que esto deba hacerse. Así que agréguelo a su grupo y una vez que lo limpie piscina, también limpia mi objeto por mí " .Con ARC, el compilador decide cuándo conservar un objeto, cuándo liberar un objeto y cuándo agregarlo a un grupo de liberación automática, pero aún requiere la presencia de grupos de liberación automática para poder devolver los objetos recién creados de los métodos sin pérdida de memoria. Apple acaba de hacer algunas optimizaciones ingeniosas para el código generado que a veces eliminará los grupos de liberación automática durante el tiempo de ejecución. Estas optimizaciones requieren que tanto la persona que llama como la persona que llama estén usando ARC (recuerde que mezclar ARC y no ARC es legal y también oficialmente compatible) y, si ese es el caso, solo se puede conocer en tiempo de ejecución.
Considere este Código ARC:
El código que genera el sistema puede comportarse como el siguiente código (que es la versión segura que le permite mezclar libremente códigos ARC y no ARC):
(Tenga en cuenta que la retención / liberación en la persona que llama es solo una retención de seguridad defensiva, no es estrictamente necesario, el código sería perfectamente correcto sin él)
O puede comportarse como este código, en caso de que se detecte que ambos usan ARC en tiempo de ejecución:
Como puede ver, Apple elimina la liberación de aire, por lo tanto, también la liberación retardada de objetos cuando se destruye el grupo, así como la retención de seguridad. Para obtener más información sobre cómo es posible y lo que realmente sucede detrás de escena, consulte esta publicación de blog.
Ahora a la pregunta real: ¿Por qué uno usaría
@autoreleasepool
?Para la mayoría de los desarrolladores, solo queda una razón hoy para usar esta construcción en su código y es mantener la huella de memoria pequeña donde corresponda. Por ejemplo, considere este bucle:
Suponga que cada llamada a
tempObjectForData
puede crear una nuevaTempObject
que se devuelve automáticamente. El ciclo for creará un millón de estos objetos temporales que se recopilarán en el grupo de liberación automática actual y solo una vez que se destruya ese grupo, también se destruirán todos los objetos temporales. Hasta que eso suceda, tiene un millón de estos objetos temporales en la memoria.Si escribes el código de esta manera:
Luego, se crea un nuevo grupo cada vez que se ejecuta el ciclo for y se destruye al final de cada iteración del ciclo. De esa manera, a lo sumo, un objeto temporal permanece en la memoria en cualquier momento a pesar del ciclo que se ejecuta un millón de veces.
En el pasado, a menudo también tenía que administrar las agrupaciones de liberación automática cuando administraba subprocesos (por ejemplo, usar
NSThread
), ya que solo el subproceso principal tiene automáticamente un grupo de liberación automática para una aplicación Cocoa / UIKit. Sin embargo, esto es más o menos el legado de hoy, ya que hoy probablemente no usaría hilos para empezar. Usaría GCDDispatchQueue
's oNSOperationQueue
' s y estos dos sí administran un grupo de liberación automática de nivel superior para usted, creado antes de ejecutar un bloque / tarea y destruido una vez hecho con él.fuente
Parece haber mucha confusión sobre este tema (y al menos 80 personas que probablemente ahora están confundidas acerca de esto y piensan que necesitan espolvorear @autoreleasepool alrededor de su código).
Si un proyecto (incluidas sus dependencias) usa exclusivamente ARC, entonces @autoreleasepool nunca necesita ser usado y no hará nada útil. ARC manejará la liberación de objetos en el momento correcto. Por ejemplo:
muestra:
Cada objeto de prueba se desasigna tan pronto como el valor queda fuera de alcance, sin esperar a que salga un grupo de liberación automática. (Lo mismo sucede con el ejemplo NSNumber; esto simplemente nos permite observar el acuerdo.) ARC no utiliza la liberación automática.
La razón por la que @autoreleasepool todavía está permitida es para proyectos mixtos ARC y no ARC, que aún no se han transferido completamente a ARC.
Si llama a código no-ARC, se puede devolver un objeto autoreleased. En ese caso, el bucle anterior se filtraría, ya que el grupo de liberación automática actual nunca se cerrará. Ahí es donde querría poner un @autoreleasepool alrededor del bloque de código.
Pero si ha realizado la transición ARC por completo, entonces olvídese de autoreleasepool.
fuente
+ (Testing *) testing { return [Testing new] }
. Entonces verás que no se llamará a dealloc hasta más tarde. Esto se soluciona si envuelve el interior del bucle en un@autoreleasepool
bloque.+ (Testing *) testing { return [Testing new];} + (void) test { while(true) NSLog(@"p = %p", [self testing]);}
[UIImage imageWithData]
a la ecuación, de repente, comencé a ver elautorelease
comportamiento tradicional , que requería@autoreleasepool
mantener la memoria máxima a un nivel razonable.