¿Se requiere @autoreleasepool para el uso moderno de iOS 8 NSOperation?
automatic-ref-counting ios8 (1)
He leído la Guía de Programación de Concurrencia
En la guía, el texto indica que las colas de envío de GCD definen sus propios grupos de @autoreleasepool y menciona que aún se recomienda definir uno a nivel de envío, pero para NSOperation no se dice nada y el código de ejemplo proporcionado por Apple tampoco muestra el uso de La estructura de @autoreleasepool. El único lugar donde @autoreleasepool se menciona vagamente en el contexto de NSOperation es en el Historial de revisiones,
2012-07-17 - Se eliminó información obsoleta sobre el uso del grupo de autorelease con las operaciones.
Mirando el código de muestra disponible en línea, por ej. http://www.raywenderlich.com/19788/how-to-use-nsoperations-and-nsoperationqueues está haciendo uso de @autoreleasepool en las implementaciones de objetos basados en NSOperations, por ejemplo:
@implementation ImageDownloader
- (void)main {
@autoreleasepool {
...
}
}
@end
- ¿Cómo debo implementar los objetos modernos de NSOperation?
- ¿A qué se refería la actualización de Apple del 2012-07-17?
Si está derivando de NSOperation
e implementando el método main
, no necesita configurar un grupo de autorelease. La implementación predeterminada del método de start
NSAutoReleasePool
un NSAutoReleasePool
, llama a main
y luego NSAutoReleasePool
el NSAutoReleasePool
. Lo mismo ocurre con NSInvocationOperation
y NSBlockOperation
, que comparten la misma implementación del método de start
.
El siguiente es un desmontaje abreviado del método de start
para NSOperation
. Tenga en cuenta las llamadas a NSPushAutoreleasePool, luego una llamada a main seguida de una llamada a NSPopAutoreleasePool:
Foundation`-[newMyObj__NSOperationInternal _start:]:
0x7fff8e5df30f: pushq %rbp
...
0x7fff8e5df49c: callq *-0x16b95bb2(%rip) ; (void *)0x00007fff8d9d30c0: objc_msgSend
0x7fff8e5df4a2: movl $0x1, %edi
; new NSAutoreleasePool is pushed here
0x7fff8e5df4a7: callq 0x7fff8e5df6d6 ; NSPushAutoreleasePool
... NSOperation main is called
0x7fff8e5df6a4: callq *-0x16b95dba(%rip) ; (void *)0x00007fff8d9d30c0: objc_msgSend
0x7fff8e5df6aa: movq %r15, %rdi
; new NSAutoreleasePool is popped here, which releases any objects added in the main method
0x7fff8e5df6ad: callq 0x7fff8e5e1408 ; NSPopAutoreleasePool
Aquí hay una instantánea de un código de ejemplo ejecutándose
-
MyObj
está asignado en el métodomain
y me aseguro de que el objeto debe ser liberado automáticamente. -
main
regresa a_start
, y la siguiente imagen muestra un seguimiento de pila conMyObj dealloc
que llama el grupo de autorelease actual, que aparece dentro de_start
Para referencia, este es el código de ejemplo que usé para verificar el comportamiento:
#import <Foundation/Foundation.h>
@interface MyObj : NSObject
@end
@implementation MyObj
- (void)dealloc {
NSLog(@"dealloc");
}
@end
@interface TestOp : NSOperation {
MyObj *obj;
}
@end
@implementation TestOp
- (MyObj *)setMyObj:(MyObj *)o {
MyObj *old = obj;
obj = o;
return old;
}
- (void)main {
MyObj *old = [self setMyObj:[MyObj new]];
[self setMyObj:old];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
NSOperationQueue *q = [NSOperationQueue new];
TestOp *op = [TestOp new];
[q addOperation:op];
[op waitUntilFinished];
}
return 0;
}
Grand Central Dispatch gestiona de manera similar los grupos de autorelease para las colas de envío, según la Guía de programación de concurrencia :
Si su bloque crea más de unos pocos objetos Objective-C, es posible que desee encerrar partes del código de su bloque en un bloque @autorelease para manejar la administración de la memoria de esos objetos. Aunque las colas de despacho de GCD tienen sus propios grupos de autorelease, no ofrecen ninguna garantía sobre cuándo se drenan esos grupos. Si su aplicación tiene memoria limitada, la creación de su propio grupo de autorelease le permite liberar la memoria para los objetos lanzados automáticamente a intervalos más regulares.