5

I have a method that does an asynchronous network call, and then passes the success or failure results back via blocks:

- (void) loginWithSuccess:(void (^)(id responseObject))success failure:(void (^)(NSError* error))failure { ... if(error) { failure(error); } else { success(responseObject); } } 

I noticed that if I call this method and pass in nil as my blocks, my application will crash with EXEC_BAD_ACCESS:

[manager loginWithWithSuccess:nil failure:nil]; 

But, if I pass in empty blocks, it runs fine:

[manager loginWithWithSuccess:^(id responseObject){} failure:^(NSError *error){}]; 

I assume this is because at runtime you can't pass parameters to nil? So, when defining my methods that take blocks, should I always check that the blocks are not nil before calling them?

4
  • 3
    The library you're using should be guarding against a nil Block, because doThatThingYouDoWithCompletion:aBlock error:nil should be the way you say "I don't want to hear about errors". Commented Jan 14, 2014 at 6:03
  • @JoshCaswell thanks, I'll put in nil checks. Also thanks for pointing to the other answer. Commented Jan 14, 2014 at 6:09
  • 2
    @RajanBalana: Uh, no. NULL == nil. Neither can be called as a function. dispatch_block_t crash = NULL; crash(); Commented Jan 14, 2014 at 6:09
  • @JoshCaswell You are right ! Commented Jan 14, 2014 at 6:11

2 Answers 2

3

Just looking at Apple's Frameworks, some methods with block parameters accept NULL/nil as the block argument (e.g. animateWithDuration:animations:completion:), others don't (e.g. enumerateObjectsUsingBlock:).

If you're designing an API, you have to make that decision. If it makes sense to not have a block, you should accept nil and check before executing the block, otherwise you should throw an assertion like [@[] enumerateObjectsUsingBlock:nil] does:

'NSInvalidArgumentException', reason: '*** -[NSArray enumerateObjectsUsingBlock:]: block cannot be nil' 

So why are you getting EXEC_BAD_ACCESS?

When invoking a block you are dereferencing the address, which you obviously can't do if it's not pointing to an actual block. There is a great explanation in this answer.

Sign up to request clarification or add additional context in comments.

1 Comment

The important difference between the two kinds of methods being, of course, can the method still do its job without the Block?
0

Please try the following example to understand call block logic:

void (^printString)(NSString*) = ^(NSString* arg) { NSLog(@"%@", arg); }; //(1) printString = ^(NSString* arg){}; //(2) printString = NULL; printString(@"1"); 

In the console you will see "1". Then uncomment (1) and console reveals "Called" but no errors! And at last, uncomment (2) and get EXEC_BAD_ACCESS. It is your situation exactly.

Called block must be not NULL or nil. You need to check existence of passing blocks in loginWithSuccess before call.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.