Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is @autoreleasepool still required for modern iOS 8 NSOperation usage?

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
  1. How should I be implementation modern NSOperation objects?
  2. What was the update Apple are referring to from 2012-07-17 ?
like image 831
Maxim Veksler Avatar asked Jul 03 '14 19:07

Maxim Veksler


People also ask

What is autoreleasepool?

An autorelease pool stores objects that are sent a release message when the pool itself is drained. Important. If you use Automatic Reference Counting (ARC), you cannot use autorelease pools directly. Instead, you use @autoreleasepool blocks.

What is autorelease pool in Swift?

The autoreleasepool allows you to explicitly manage when autorelease objects are deallocated in Swift, just like you were able to in Objective-C. Note: When dealing with Swift native objects, you generally will not receive autorelease objects.


1 Answers

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

  1. MyObj is allocated in the main method and I make sure the object must be autoreleased
  2. main returns to _start, and the following image shows a stack trace with MyObj dealloc being called by the current autorelease pool, popped inside _start

call stack showing object is released when NSPopAutoreleasePool is called

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.

like image 179
Stuart Carnie Avatar answered Sep 29 '22 05:09

Stuart Carnie