I'm using Xcode 4.3.3 and developing for iOS 5.0+. In development of an ARC iOS application, I've started using blocks as a callback mechanism for asynchronous operations. The app works fine in the simulator and on the device.
Then I ran it the profiler for the first time, and it started crashing on me nearly right away - in particular, an EXC_BAD_ACCESS when trying to invoke the first callback block.
After a little investigation, it was clear the difference in behavior was because the profiler runs in "Release mode" by default - in particular, with Optimization Level set to "Fastest, Smallest [-Os]" instead of "None [-O0]".
For example, the following code (simplified for this question) would crash when trying to execute the callbackBlock:
- (void) setCallbackBlock:(void (^)(NSString *input))block
{
callbackBlock = block;
}
- (void) invokeCallbackWithInput:(NSString *)input
{
if (callbackBlock) {
callbackBlock(input);
}
}
Debugging into it, calling setCallbackBlock with optimization level set to "None", the incoming block would be an NSStackBlock
, and the callbackBlock would become an NSMallocBlock
.
However, with Optimization Level "Fastest, Smallest", it remained an NSStackBlock
.
Changing the setter code to use [block copy]
fixes the crashing problem (based on iOS 5 blocks crash only with Release Build).
However, another related question indicates that this shouldn't be necessary with ARC - block variables are copied to the heap in ARC - Why does Objective-C block still work without copying it to the heap?
So my question: What's going on here, and why? (Also, how can both of those answers be correct...?)
Edit: To clarify how callbackBlock is being declared - just above my @implementation where those methods are is this:
@interface MyClass ()
{
void (^callbackBlock)(NSString *input);
}
@end