2

I have an app that I am refactoring and I just implemented multithreading so that the UI may run smoother. In the iphone simulator I don't get any leaks but testing on my iPhone 3G running on iOS 4.2 I get a memory leak. I have done a lot of searching for the correct way to implement an autoreleasepool with an operationqueue, help will be greatly appreciated.

I have created an nsoperationqueue in my viewcontroller as such

- (void)loadData
{
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  NSOperationQueue *queue = [NSOperationQueue new];  // creates multithread for loading data without slowing UI
  NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(firstRun) object:nil];

  [queue addOperation:operation];
  [operation release];
  [queue release];
  [pool release];
}
Jason Coco
  • 77,985
  • 20
  • 184
  • 180
zambono
  • 21
  • 1
  • 2

2 Answers2

2

First, you shouldn't just create and then release the queue. It's more natural to create that queue as one of your class's ivars and then release it when your view controller goes away (you can also cancel up any pending operations and cancel/wait for any running operations to complete).

Second, you don't need the autorelease pool in the method that creates the operation and adds it in the queue since that method is being called from the main thread. Instead, you need the autorelease pool from the method your object actually calls (this is what may be running on another thread).

So, you might have the following (assuming you name your queue ivar queue_):

- (void)viewDidLoad
{
  [super viewDidLoad];

  if( !queue_ ) queue_ = [[NSOperationQueue alloc] init];
  // other view loading code
}

- (void)loadData
{
  NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(firstRun) object:nil];
  [queue_ addOperation:operation];
  [operation release];
}

- (void)firstRun
{
  // Here we may run on another thread, so 1) we need an autorelease pool; and
  // 2) we need to make sure we don't do anything that requires a runloop
  NSAutoreleasePool* threadPool = [NSAutoreleasePool new];

  // do long-running things

  [threadPool drain];
}

- (void)dealloc
{
  if( queue_ ) {
    [queue_ setSuspended:YES];
    [queue_ cancelAllOperations];
    // you need to decide if you need to handle running operations
    // reasonably, but don't wait here because that may block the
    // main thread
    [queue_ release];
  }
  // other dealloc stuff
  [super dealloc];
}

You could also initialize your queue on demand, so instead of initializing in viewDidLoad, check for its existence and initialize if necessary anywhere you'd add an operation. This might be wrapped in a method call of its own, but lazy initialization here probably isn't really necessary as queues are pretty light-weight.

Jason Coco
  • 77,985
  • 20
  • 184
  • 180
  • thanks Jason, will any methods called after the threadpool init be automatically using it? also do i have to release threadpool. I see that its initiated with new. – zambono Dec 31 '10 at 22:32
  • @zambono: Yes, it should be the first line and you should drain the pool as the last line in your method. The "new" method is just a shortcut for alloc/init, so [NSObject new] is the same as [[NSObject alloc] init], so you are responsible for it. However, autorelease pools are a little special. The drain method there releases the pool as well, so you don't have to send it a specific release message (if you do, it just ignores it). – Jason Coco Dec 31 '10 at 23:01
0

You should create an NSAutoreleasePool at the start of the method that the NSOperation will invoke (in this case, firstRun), and drain it at the end of the method.

Richard
  • 3,316
  • 30
  • 41