Danra
Danra

Reputation: 9916

Is it safe to autorelease objects inside an NSOperation?

I am running NSInvocationOperation-type operations in an NSOperationQueue and was wondering if it is safe to autorelease objects - That is, if it is guaranteed that the thread started for each operation has its own autorelease pool.

I didn't find any documentation autorelease pools for operations - Reading Apple's docs actually suggests I do need to define my own autorelease pool.

However: 1) I can't see any leaks in instruments, at least not more than when I allocate my own autorelease pool in the operation.

2) looking in the debugger I can see this stack trace:

#0  0x00fc3e82 in -[NSObject(NSObject) release] ()
#1  0x00faaa6c in CFRelease ()
#2  0x00fbf804 in __CFBasicHashDrain ()
#3  0x00faabcb in _CFRelease ()
#4  0x00fcfb8d in _CFAutoreleasePoolPop ()
#5  0x000edd0d in -[__NSOperationInternal start] ()
#6  0x000ed826 in ____startOperations_block_invoke_2 ()
#7  0x94358024 in _dispatch_call_block_and_release ()
#8  0x9434a2f2 in _dispatch_worker_thread2 ()
#9  0x94349d81 in _pthread_wqthread ()
#10 0x94349bc6 in start_wqthread ()

So it looks as though there is a CFAutoreleasePool - Is it safe to assume this object will call release on all my autoreleased objects when the operation is complete?

Upvotes: 3

Views: 1529

Answers (1)

user557219
user557219

Reputation:

I’ve written a small program to test whether NSInvocationOperation would create an autorelease pool for the operation:

#import <Foundation/Foundation.h>

@interface MyClass : NSObject
@end

@implementation MyClass
- (void)performSomeTask:(id)data
{
    NSString *s = [[[NSString alloc] initWithFormat:@"hey %@", data]
        autorelease];
    if ([[NSThread currentThread] isMainThread])
        NSLog(@"performSomeTask on the main thread!");
    else
        NSLog(@"performSomeTask NOT on the main thread!");

    NSLog(@"-- %@", s);
}
@end

int main(int argc, char *argv[]) {
  MyClass *c = [MyClass new];

  if (argc == 2 && strcmp(argv[1], "nop") == 0)
      [c performSomeTask:@"ho"];
  else {
      NSInvocationOperation *op = [[NSInvocationOperation alloc]
          initWithTarget:c
                selector:@selector(performSomeTask:)
                  object:@"howdy"];
      NSOperationQueue *queue  = [[NSOperationQueue alloc] init];
      [queue addOperation:op];
      [op waitUntilFinished];

      [op release];
      [queue release];
  }

  [c release];

  return 0;
}

It works as follows: if "nop" is passed on the command-line, it will execute -performSomeTask: directly, on the main thread, with no autorelease pool in place. The resulting output is:

$ ./c nop
*** __NSAutoreleaseNoPool(): Object 0x10010cca0 of class NSCFString autoreleased with no pool in place - just leaking
performSomeTask on the main thread!
-- hey ho

The autoreleased string in -performSomeTask: causes a leak.

Running the program without passing "nop" will execute -performSomeTask: via an NSInvocationOperation on a different thread. The resulting output is:

$ ./c
*** __NSAutoreleaseNoPool(): Object 0x100105ec0 of class NSInvocation autoreleased with no pool in place - just leaking
*** __NSAutoreleaseNoPool(): Object 0x100111300 of class NSCFSet autoreleased with no pool in place - just leaking
*** __NSAutoreleaseNoPool(): Object 0x100111b60 of class NSCFSet autoreleased with no pool in place - just leaking
*** __NSAutoreleaseNoPool(): Object 0x100105660 of class NSCFSet autoreleased with no pool in place - just leaking
performSomeTask NOT on the main thread!
-- hey howdy

As we can see, there are instances of NSInvocation and NSSet that are leaking but the autoreleased string in -performSomeTask: isn’t leaking, hence an autorelease pool was created for that invocation operation.

I think it’s safe to assume that NSInvocationOperation (and probally all NSOperation subclasses in Apple’s frameworks) create their own autorelease pools, just like the Concurrency Programming Guide suggests for custom NSOperation subclasses.

Upvotes: 4

Related Questions