This crash is quite rare, but it happened often enough to convince me that I am doing it wrong.
This is an API call made using dispatch of asynchronous main-thread messages and sending a barrier in a user-side parallel queue (the barrier is used because we modify data that is read elsewhere through unsafe calls in one queue).
The goal is to make an HTTP request asynchronously, and then run dispatch_barrier_async to process the received data.
An accident occurs when the dispatch_barrier_async call copies the variables that are used in the passed block. I believe that the variable was freed before it was used in the block, but given how I declare it (as the __block variable), I donโt see how this can happen (if the problem is not with the other side of the assignment operator .. .).
Here's the stack trace:
#0 0x00004f44 in __Block_byref_object_copy_ at /blah/ABEvent.m:156
And the code:
+ (void)fetch { ABBlock _success = ^(ABMessage *m) { __block NSMutableArray *fetched = [NSMutableArray arrayWithArray:m.params[@"live"]]; [fetched addObjectsFromArray:m.params[@"soon"]]; // EXC_BAD_ACCESS (top of stack) dispatch_barrier_async([ABEvent eventQueue], ^{ // CRASHED IN BLOCK INVOKE (stack line 6) NSMutableArray *events = [NSMutableArray array]; for (NSDictionary *d in fetched) { [events addObject:[ABEvent eventWithDictionary:d]]; } AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate; appDelegate.events = events; events = nil; }); }; [[ABClient sharedInstance] events:_success failure:nil]; }
Upstream:
-(void)events:(ABBlock)success failure:(ABBlock)failure { NSString *params = [NSString stringWithFormat:@"%@",[ABUser loggedInUser]? [ABUser loggedInUser].name : @"no"]; NSDictionary *dict = @{@"loggedIn": params}; [self get:@"events/live.json" parameters:dict success:success failure:failure]; } - (void)get:(NSString *)path parameters:(NSDictionary *)parameters success:(ABBlock)success failure:(ABBlock)failure __block ABBlock blockSuccess = success; __block ABBlock blockFailure = failure; NSString *blockPath = path; NSDictionary *blockParameters = parameters; AFHTTPSuccessBlock _success = ^(AFHTTPRequestOperation *request, id response) { if (blockSuccess) { ABMessage *msg = [ABMessage messageWithObject:response]; dispatch_async(dispatch_get_main_queue(), ^(void) { blockSuccess(msg); }); } }; } Elsewhere: typedef void (^ABBlock) (ABMessage *);
Is there something clearly wrong with the way the variables are used by the async_array block? I am wondering if I need to copy the message (* m) as an argument.
source share