NSDictionary of Blocks as argument causes overflow

I understand that blocks are objective c objects and can be placed directly into NSDictionary without Block_copy when using ARC. But with this code, I got an EXC_BAD_ACCESS error:

- (void)viewDidLoad { [super viewDidLoad]; [self method1:^(BOOL result){ NSLog(@"method1WithBlock finished %d", result); }]; } - (void) method1:(void (^)(BOOL))finish{ NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:^(NSData *rcb){ finish(YES); }, @"success", ^(NSError *error){ finish(NO); }, @"failure", nil]; [self method2:dict]; } - (void) method2:(NSDictionary *)dict{ void (^success)(NSData *rcb) = [dict objectForKey:@"success"]; success(nil); } 

If I change method1: there is no error for this.

 - (void) method1:(void (^)(BOOL))finish{ void (^success)(NSData *) = ^(NSData *rcb){ finish(YES); }; void (^failure)(NSError *error) = ^(NSError *error){ finish(NO); }; NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:success, @"success", failure, @"failure", nil]; [self method2:dict]; } 

Can someone explain why I should use automatic variables to store blocks before putting them into the dictionary?

I am using iOS SDK 6.1.

+4
source share
3 answers

According to Going to ARC Release Notes, you need to copy the saved block in the dictionary (emphasis mine):

Blocks "just work" when you pass blocks up the stack in ARC mode, such as in return. You no longer need to call Block Copy. You still need to use [^{} copy] when passing down to the arrayWithObjects: stack and other methods that save .

The second method works "randomly" because success and failure are __NSGlobalBlock__ , not the "stack-based block" that needs to be copied to the heap.

+5
source

I understand that blocks are objective c objects and can be placed directly into NSDictionary without Block_copy when using ARC.

No, these are not ordinary objects. When you create a block, it is on the stack, and it does not matter what it saves when you exit the function that it pops from the stack. Copy it to keep it alive.

0
source

You must copy the blocks before passing them to the method, when 1) the block will be stored longer than the duration of the call, and 2) the parameter with which you pass it is the usual type of object pointer (i.e. id or NSObject * ) instead of the block type .

This applies to your call. dictionaryWithObjectsAndKeys: stores the argument in the resulting dictionary and simply expects the usual arguments to the object pointer (of type id ) and does not know if you are passing blocks or not.

The reason for the second condition that I talked about is that if the method parameter already accepts a block type (for example, for any parameters of the completion handler), then this method already knows about the special requirements for managing block memory, and therefore will take responsibility for copying a block if it needs to be saved. In this case, the caller does not need to worry about it. However, in your case, you pass it to a general method that does not know that it receives the block, and therefore does not know to copy it. Therefore, the caller must do this.

Can someone explain why I should use automatic variables to store blocks before putting them into a dictionary?

About this, for example, your second example works because the latest versions of the ARC compiler are very conservative with respect to blocks and insert copies whenever you assign it to a variable of type block. However, this is not guaranteed by the ARC specification and is not guaranteed to work in the future or in another compiler. You should not rely on this behavior.

0
source

Source: https://habr.com/ru/post/1492468/


All Articles