Autorelease pools y cuando se llama a la versión bajo iOS

Quería aclarar algo.

Digamos que tengo el siguiente código:

- (void) viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; for (int i = 0; i < 5000000; i++) { NSString *s = [NSString stringWithFormat:@"Hello, %@!", @"World"]; } } 

Esto creará 5 millones de cadenas autoelevadas dentro de esta llamada de function. Esperaba que esto mantuviera esos objects hasta la finalización de la aplicación, ya que el único @autoreleasepool que veo es el que envuelve la instanciación de la aplicación en main.m. Pero ese no es el caso. Al final de esta llamada de function, parece que todos reciben su liberación llamada y se eliminan de la memory.

Este documento:

https://developer.apple.com/library/mac/documentation/cocoa/reference/foundation/Classes/NSAutoreleasePool_Class/Reference/Reference.html

Indica que "el Kit de aplicaciones crea un set de autorelease en el hilo principal al comienzo de cada ciclo del ciclo de events y lo drena al final, liberando de este modo los objects autoelevados generados mientras se procesa un evento".

Eso tiene sentido para mí, pero esto está bajo UIKit, y no el Kit de aplicaciones. Mi pregunta es: ¿UIKit / Cocoa Touch hace lo mismo en este caso, o hay otra explicación para que mis objects sean liberados?

¡Gracias!

Sí, UIKit hace lo mismo. El set de autorelease del subprocess principal creado por el sistema se drena al final de cada ciclo de ciclo de ejecución. Probablemente sea mejor no confiar en esta vida exacta en su propio código. Si creas un hilo nuevo manualmente (usando, por ejemplo, NSThread), eres el responsable de crear el pool de autorelease en ese hilo.

EDITAR: la respuesta de Rob proporciona alguna buena información adicional sobre el comportamiento en ARC. En general, es justo decir que los objects tienen less probabilidades de terminar en el grupo de autorelease debido a algunas optimizaciones que ARC puede hacer.

Andrew respondió a su pregunta principal que, sí, su grupo de autorelease se drenará en cada ciclo del ciclo de ejecución principal. Por lo tanto, cualquier object de autorelease creado en viewDidLoad puede viewDidLoad rápidamente cuando regrese al bucle de ejecución principal. Ciertamente, no se retendrán "hasta la finalización de la request".

Pero debemos tener cuidado: está asumiendo claramente que estos objects se están agregando a un set de autorelease. Algunas advertencias a esta suposition:

  1. En el pasado (y todavía se requiere para la interoperabilidad ARC-MRC), cuando se devuelven objects de methods cuyos nombres no comenzaron con alloc , new , copy o mutableCopy , esos objects autorizarán objects, se desasignarán solo cuando se drene el grupo de autorelease (es decir, cuando regrese al ciclo de ejecución).

  2. Pero ARC se ha vuelto más inteligente sobre cómo minimizar la necesidad de agrupaciones autorelease (ver http://rentzsch.tumblr.com/post/75082194868/arcs-fast-autorelease , que discute callerAcceptsFastAutorelease , ahora llamado callerAcceptsOptimizedReturn invocado por prepareOptimizedReturn ), por lo que a menudo lo hará No veo este comportamiento autorelease . Por lo tanto, si tanto la biblioteca como la persona que llama están usando ARC, es posible que los objects no se coloquen en el grupo de autorelease, sino que ARC los libere inteligentemente de inmediato si no lo necesitan.

    Con proyectos ARC contemporáneos, por lo general no se necesitan piscinas autorelease. Pero en ciertos casos especiales, uno todavía puede beneficiarse del uso de pools de autorelease. Esquemaré uno de esos casos a continuación.

Considere el siguiente código:

 #import "ViewController.h" #import <sys/kdebug_signpost.h> typedef enum : NSUInteger { InnerLoop = 1, MainLoop = 2 } MySignPostCodes; @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"png"]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ kdebug_signpost_start(MainLoop, 0, 0, 0, 1); for (int j = 0; j < 500; i++) { NSData *data = [NSData dataWithContentsOfURL:fileURL]; UIImage *image = [[UIImage alloc] initWithData:data]; NSLog(@"%p", NSStringFromCGSize(image.size)); // so it's not optimized out [NSThread sleepForTimeInterval:0.01]; } kdebug_signpost_end(MainLoop, 0, 0, 0, 1); }); } @end 

El siguiente código agregará 500,000 objects a la agrupación de autorelease, que solo se drenará cuando ceda al ciclo de ejecución:

sin grupo

En este caso, puede utilizar una agrupación autorelease para minimizar la marca de agua alta:

 @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"png"]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ kdebug_signpost_start(MainLoop, 0, 0, 0, 1); for (int j = 0; j < 5; j++) { @autoreleasepool { kdebug_signpost_start(InnerLoop, 0, 0, 0, 2); for (long i = 0; i < 100; i++) { NSData *data = [NSData dataWithContentsOfURL:fileURL]; UIImage *image = [[UIImage alloc] initWithData:data]; NSLog(@"%p", NSStringFromCGSize(image.size)); // so it's not optimized out [NSThread sleepForTimeInterval:0.01]; } kdebug_signpost_end(InnerLoop, 0, 0, 0, 2); } } kdebug_signpost_end(MainLoop, 0, 0, 0, 1); }); } @end 

piscina

En pocas palabras, con ARC, no siempre es obvio cuando usó un object de autorelease y cuando lo libera explícitamente cuando la variable queda fuera de scope. Siempre puede confirmar esto examinando el comportamiento en Instrumentos.

Como un aparte, me desconfiaría de sacar demasiadas conclusiones generales de administración de la memory al usar la class NSString , ya que ha sido altamente optimizado y no siempre se ajusta a las prácticas de administración de memory estándar.

Supongo que cuando asigna un object nuevo a una reference que solía contener un object, el object original se libera inmediatamente (si nada más apunta a él, el recuento de ref va a cero) usando ARC y asumiendo strong reference strong pnetworkingeterminada como en su Ejemplo de bucle.

 MyObject *object = [[MyObject alloc] init]; // obj1, ref count 1 because strong object = [[MyObject alloc] init]; // obj2, ref count of obj1 should be 0 // so obj1 gets released 

Notas de Apple en la transición a las notas de la versión de ARC

Intente dejar de pensar acerca de dónde se ponen las llamadas de retención / liberación y piense en sus algorithms de aplicación en su lugar. Piense en pointers "fuertes y débiles" en sus objects, sobre la propiedad del object y sobre posibles ciclos de retención.

Suena que la release se invoca en un object cuando se le asigna un nuevo valor, de la documentation de Clang 3.4 de Clang OBJECTIVE-C COUNTING AUTOMÁTICO DE REFERENCIA (ARC)

La asignación ocurre cuando se evalúa un operador de asignación. La semántica varía según la calificación:

Para __ objects fuertes, primero se retiene la nueva punta; segundo, el lvalue está cargado de semántica primitiva; Tercero, la nueva punta se almacena en el lvalor con semántica primitiva; y, finalmente, se lanza la punta vieja. Esto no se realiza atómicamente; se debe usar la synchronization externa para que esto sea seguro frente a las cargas y almacenes concurrentes.