Samhan Salahuddin
Samhan Salahuddin

Reputation: 2170

Weird bad_access with dispatch_async

I'm getting a really weird bad access error while using dispatch async. I managed to reduce it down to this segment of code in my program.

-(void)buttonTapped:(id)sender {

    __block NSArray*foo = nil;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        //Foo was initially declared here but then moved it outside.
        foo = [self someMethod];
        [foo retain]; // bad access here. Why ?
        dispatch_async(dispatch_get_main_queue(),0) {
            // doesnt matter what happens here
    }); });
}

-(id)someMethod 
{
    return [self secondMethod];
}

-(id)secondMethod
{
    // was initially returning an autoreleased object from here. Changed it
    // to eliminate that as source of the error.
    id newThing = [[NSObject alloc] init];
    return newThing;
}

The code didnt initially look like this but this is how it is right now . Including allocating a dummy NSObject .

How is it possible for foo to get released in between calls inside a dispatch async ? I dont understand how this is possible. I know its difficult to suggest whats going from just this but any debugging suggestions would be helpful. I tried turning on NSZombies but I dont get any Zombies.

Upvotes: 0

Views: 597

Answers (1)

Rob
Rob

Reputation: 438307

You ask:

How is it possible for foo to get released in between calls inside a dispatch_async?

It shouldn't, unless someMethod or secondMethod are, themselves, doing something asynchronously which might allow the autorelease pool to be drained in the interim.

I tried turning on NSZombies but I dont get any Zombies.

If you've got zombies turned on and you're not getting a zombie, then I suspect the problem rests elsewhere. Frankly, I suspect that the root of the problem was eliminated in your process of simplifying the sample code for the purposes of the question:

A few other observations/clarifications:

  1. You declared foo to be a NSArray, but then you're returning NSObject. I'll assume you meant it to be NSObject throughout.

  2. You have a line of code that says:

    dispatch_async(dispatch_get_main_queue(),0) {
    

    I'll just assume that was a typo and that you intended:

    dispatch_async(dispatch_get_main_queue(), ^{
    
  3. The foo variable should definitely be inside the dispatch_async block. It doesn't really make sense to have a __block variable for something (a) you don't reference outside of that block for a block; and (b) for a block you're dispatching asynchronously.

  4. The secondMethod should return an autorelease object, as you apparently originally had it. (Or you'd probably want to change secondMethod and someMethod to start with new in their names to avoid confusion and make life easier for yourself when you eventually move to ARC.)

  5. If you retain the foo object, you'll want to also add the appropriate release. In fact, your original code sample returns a +1 object, and then retain it again, bumping it to +2, so you'd need two release calls.

Anyway, correcting for these various issues, I end up with the following, which does not generate an exception:

- (IBAction)buttonTapped:(id)sender
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{

        NSObject *foo = [self someMethod];
        [foo retain]; // no bad access here

        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"foo = %@", foo);

            [foo release];
        });
    });
}

- (NSObject *)someMethod
{
    return [self secondMethod];
}

- (NSObject *)secondMethod
{
    return [[[NSObject alloc] init] autorelease];
}

Furthermore, I would suggest, especially when using manual retain and release (MRR), that you run it through the static analyzer ("Analyze" on the Xcode "Product" menu) and make sure you have a clean bill of health. (It would have pointed out some of the issues I mentioned.) It's not perfect, but it's remarkably good at identifying issues.

But, in short, the above code is fine, and if you're still getting an exception, update your question with working code that reproduces the exception.

Upvotes: 1

Related Questions