I’ve read through Concurrency Programming Guide
In the guide the text states that GCD dispatch queues define their own @autoreleasepool pools and mentions that it’s still recommended to define one at a per dispatch level, yet for NSOperation nothing is said and the example code provided by Apple also does not show usage of the @autoreleasepool structure. The only place where @autoreleasepool is vaguely mentioned in the context of NSOperation is in the Revision History,
2012-07-17 - Removed obsolete information about autorelease pool usage with operations.
Looking at sample code available online, for ex. http://www.raywenderlich.com/19788/how-to-use-nsoperations-and-nsoperationqueues is making usage of @autoreleasepool in the implementations of NSOperations based objects, for example:
@implementation ImageDownloader
- (void)main {
@autoreleasepool {
...
}
}
@end
If you are deriving from NSOperation
and implementing the main
method, you do not need to set up an autorelease pool. The default implementation of the start
method pushes an NSAutoReleasePool
, calls main
and then subsequently pops the NSAutoReleasePool
. The same goes for NSInvocationOperation
and NSBlockOperation
, which share the same implementation of the start
method.
The following is an abridged disassembly of the start
method for NSOperation
. Note the calls to NSPushAutoreleasePool, then a call to main followed by a call to 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
Here is a snapshot of some example code running
MyObj
is allocated in the main
method and I make sure the object must be autoreleasedmain
returns to _start
, and the following image shows a stack trace with MyObj dealloc
being called by the current autorelease pool, popped inside _start
For reference, this is the example code I used to verify the behavior:
#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 similarly manages autorelease pools for dispatch queues, per the Concurrency Programming Guide:
If your block creates more than a few Objective-C objects, you might want to enclose parts of your block’s code in an @autorelease block to handle the memory management for those objects. Although GCD dispatch queues have their own autorelease pools, they make no guarantees as to when those pools are drained. If your application is memory constrained, creating your own autorelease pool allows you to free up the memory for autoreleased objects at more regular intervals.