objective c - ¿Por qué se necesita @autoreleasepool con ARC?
objective-c memory-management (6)
En su mayor parte con ARC (conteo automático de referencias), no necesitamos pensar en la gestión de la memoria en absoluto con objetos Objective-C. Ya no está permitido crear NSAutoreleasePool
, sin embargo, hay una nueva sintaxis:
@autoreleasepool {
…
}
Mi pregunta es, ¿por qué necesitaría esto cuando no se supone que debo liberar / autorrellenar manualmente?
EDITAR: Para resumir lo que obtuve de todos los comentarios y comentarios de manera sucinta:
Nueva sintaxis:
@autoreleasepool { … }
es una nueva sintaxis para
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
…
[pool drain];
Más importante:
- ARC usa
autorelease
yrelease
. - Necesita un grupo de versiones automáticas para hacerlo.
- ARC no crea el grupo de versiones automáticas para usted. Sin embargo:
- El hilo principal de cada aplicación Cocoa ya tiene un grupo de autorrelease.
- En dos ocasiones, es posible que desee utilizar
@autoreleasepool
:- Cuando se encuentre en un hilo secundario y no haya un grupo de autorizaciones automáticas, debe hacer las suyas propias para evitar fugas, como
myRunLoop(…) { @autoreleasepool { … } return success; }
myRunLoop(…) { @autoreleasepool { … } return success; }
. - Cuando desee crear un grupo más local, como @mattjgalloway ha mostrado en su respuesta.
- Cuando se encuentre en un hilo secundario y no haya un grupo de autorizaciones automáticas, debe hacer las suyas propias para evitar fugas, como
ARC no se deshace de los retiros, lanzamientos y autorreleases, solo agrega los necesarios para usted. Por lo tanto, todavía hay llamadas para retener, todavía hay llamadas para lanzar, todavía hay llamadas para liberación automática y todavía hay grupos de versiones automáticas.
Uno de los otros cambios que hicieron con el nuevo compilador Clang 3.0 y ARC es que reemplazaron a NSAutoReleasePool
con la directiva del compilador @autoreleasepool
. NSAutoReleasePool
siempre fue un poco un "objeto" especial de todos modos y lo hicieron de modo que la sintaxis de usar uno no se confunde con un objeto, por lo que en general es un poco más simple.
Entonces, básicamente, necesita @autoreleasepool
porque todavía hay grupos de versiones de auto para preocuparse. Simplemente no necesita preocuparse por agregar llamadas de autorelease
.
Un ejemplo de uso de un grupo de versiones automáticas:
- (void)useALoadOfNumbers {
for (int j = 0; j < 10000; ++j) {
@autoreleasepool {
for (int i = 0; i < 10000; ++i) {
NSNumber *number = [NSNumber numberWithInt:(i+j)];
NSLog(@"number = %p", number);
}
}
}
}
Un ejemplo enormemente artificial, claro, pero si no @autoreleasepool
el @autoreleasepool
dentro del exterior for
-loop, estarías liberando 100000000 objetos más adelante en vez de 10000 cada vez for
salga el exterior for
-loop.
Actualización: También vea esta respuesta - https://.com/a/7950636/1068248 - por qué @autoreleasepool
no tiene nada que ver con ARC.
Actualización: Eché un vistazo a los aspectos internos de lo que está sucediendo aquí y lo escribí en mi blog . Si @autoreleasepool
un vistazo allí, entonces verás exactamente lo que está haciendo ARC y cómo el nuevo estilo @autoreleasepool
y cómo introduce un alcance es utilizado por el compilador para inferir información sobre lo que se requiere, liberaciones y autorreleases.
Autorelease Pool Blocks and Threads
Cada subproceso en una aplicación Cocoa mantiene su propia pila de bloques de agrupación de liberación automática. Si está escribiendo un programa exclusivo de Foundation o si separa un hilo, necesita crear su propio bloque de pool de liberación automática.
Si su aplicación o thread es de larga duración y potencialmente genera una gran cantidad de objetos liberados automáticamente, debe usar bloques de grupo de liberación automática (como AppKit y UIKit do en el hilo principal); de lo contrario, los objetos liberados automáticamente se acumulan y su huella de memoria aumenta. Si su subproceso desconectado no hace llamadas Cocoa, no necesita usar un bloque de agrupación de liberación automática.
Nota: Si crea subprocesos secundarios utilizando las API de subprocesos de POSIX en lugar de NSThread, no puede usar Cocoa a menos que Cocoa esté en modo de subprocesamiento múltiple. Cocoa ingresa al modo multihebra solo después de separar su primer objeto NSThread. Para utilizar Cocoa en subprocesos POSIX secundarios, su aplicación primero debe separar al menos un objeto NSThread, que puede salir inmediatamente. Puede probar si Cocoa está en modo multithreading con el método de clase NSThread isMultiThreaded.
...
En el recuento automático de referencias, o ARC, el sistema usa el mismo sistema de recuento de referencias que MRR, pero inserta las llamadas de método de administración de memoria adecuadas en tiempo de compilación. Le recomendamos encarecidamente que use ARC para nuevos proyectos. Si utiliza ARC, normalmente no es necesario comprender la implementación subyacente descrita en este documento, aunque en algunas situaciones puede ser útil. Para obtener más información sobre ARC, consulte Transición a Notas de versión de ARC.
Es porque todavía necesita proporcionar al compilador pistas sobre cuándo es seguro que los objetos liberados automáticamente salgan del alcance.
La gente a menudo entiende mal ARC por 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 (todas las retains
y releases
, etc.) se puede automatizar completamente en tiempo de compilación . Esto es, simplemente leyendo el código, ¡incluso antes de que se ejecute! :)
Para hacerlo solo hay una condición: DEBEMOS seguir las rules , de lo contrario, el compilador no podría automatizar el proceso en tiempo de compilación. Por lo tanto, para garantizar que nunca rompamos las reglas, no podemos escribir explícitamente la release
, retain
, etc. Esas llamadas son automáticamente inyectadas en nuestro código por el compilador. Por lo tanto, internamente todavía tenemos autorelease
s, retain
, release
, etc. Es solo que no necesitamos 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 tenemos @autoreleasepool{...}
porque al no romper ninguna de las reglas, somos libres de crear / drenar nuestro grupo en cualquier momento que lo necesitemos :).
Parece que hay mucha confusión sobre este tema (y al menos 80 personas que probablemente ahora están confundidas acerca de esto y piensan que necesitan rociar @autoreleasepool alrededor de su código).
Si un proyecto (incluidas sus dependencias) usa exclusivamente ARC, entonces @autoreleasepool nunca necesita ser utilizado y no hará nada útil. ARC se encargará de soltar objetos en el momento correcto. Por ejemplo:
@interface Testing: NSObject
+ (void) test;
@end
@implementation Testing
- (void) dealloc { NSLog(@"dealloc"); }
+ (void) test
{
while(true) NSLog(@"p = %p", [Testing new]);
}
@end
muestra:
p = 0x17696f80
dealloc
p = 0x17570a90
dealloc
Cada objeto de prueba se desasigna en cuanto el valor sale del alcance, sin esperar a que salga un grupo de autorrelease. (Lo mismo sucede con el ejemplo de NSNumber; esto solo nos permite observar el dealloc.) ARC no usa la liberación automática.
La razón por la que @autoreleasepool aún está permitido es para proyectos ARC mixtos y proyectos que no son ARC, que aún no han cambiado por completo a ARC.
Si llama al código que no es ARC, puede devolver un objeto liberado automáticamente. En ese caso, el ciclo anterior se fugaría, ya que nunca se cerrará el grupo de autorrelease actual. Ahí es donde te gustaría poner un @autoreleasepool alrededor del bloque de código.
Pero si ha realizado completamente la transición ARC, entonces olvídese de la liberación automática.
@autoreleasepool
no libera automáticamente nada. Crea un grupo de autorrelease, de modo que cuando se llegue al final del bloque, cualquier objeto que haya sido liberado automáticamente por ARC mientras el bloque estaba activo recibirá mensajes de lanzamiento. La Guía de programación de administración de memoria avanzada de Apple lo explica así:
Al final del bloque de bloque de liberación automática, los objetos que recibieron un mensaje de liberación automática dentro del bloque reciben un mensaje de liberación: un objeto recibe un mensaje de liberación para cada vez que se envió un mensaje de liberación automática dentro del bloque.