Reputation: 2170
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
Reputation: 438307
You ask:
How is it possible for
foo
to get released in between calls inside adispatch_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:
You declared foo
to be a NSArray
, but then you're returning NSObject
. I'll assume you meant it to be NSObject
throughout.
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(), ^{
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.
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.)
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