Search code examples
iphoneobjective-cmemory-managementautoreleaseretain

Multithreaded iPhone app crashes with [NSAutoreleasePool release]


I have a memory management-related question in a multithreaded iPhone application. Let's say we have this method, that is called in a separate thread than the main-UI-thread:

- (BOOL)fetchAtIndex:(NSUInteger)index
{
    NSURL *theURL = [NSURL URLWithString:[queryURLs objectAtIndex:index]];
    // Pay attention to this line:
    NSData *theData = [[NetworkHelper fetchFromNetwork:theURL] retain];

    // Some code here...

    // Now what should I do before returning result?
    //[theData release]; ??
    //[theData autorelease]; ??
    return YES;
}

As you can see, I'm retaining the NSData I got back from my network operation. The question is: why shouldn't I release (or autorelease) it at the end of my method? The only way I made it work is to use retain at first, and nothing then. If I use any other combination (nothing at all; retain then release or autorelease), my program crashes with EXC_BAD_ACCESS when I release the thread's NSAutoreleasePool. What am I missing?

FYI, here is the main code for the thread:

- (void)threadedDataFetching;
{
    // Create an autorelease pool for this thread
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    // Reload data in separate thread
    [self fetchAtIndex:0];

    // Signal the main thread that fetching is finished
    [self performSelectorOnMainThread:@selector(finishedFetchingAll) withObject:nil waitUntilDone:NO];

    // Release all objects in the autorelease pool
    [pool release]; // This line causes EXC_BAD_ACCESS
}

Thanks for your help!


Solution

  • You must not release things you have not retained yourself (using either retain or implictly by methods with: init, new, or copy in their name).

    If you retain result from fetchFromNetwork, then you must release it. Both release and autorelease should work (don't touch object after release, it's safest to set fields/variables to nil after release).

    If you're not keeping the data, then you don't even need to retain it. [NetworkHelper fetchFromNetwork] should return autoreleased object. Body of fetchFromNetwork could look like this:

    NSData *data = [[NSData alloc] init];
    // stuff happens
    return [data autorelease];
    

    or

    NSData *data = [otherObject dataFromOtherObject];
    // stuff happens
    return data; // don't (auto)release, since you haven't retained
    

    If in doubt, err on the leaky side and run application via "Leaks" Instruments or LLVM checker.