Callback lock with EXC_BAD_ACCESS

I have a custom subclass of NSOperation that I use to create HTTP requests. It accepts a blocked callback that executes when NSOperation completes. Everything works accordingly, but I experience a weird, intermittent failure while trying to complete the completion callback. I read a lot of EXEC_BAD_ACCESS block problems caused by incorrect copying of a block when passing it to additional methods.

I believe my problem is with how I use blocks. I will include a standard use case for my application below. The root of my problem is probably up to a misunderstanding of the property, where are the blocks.

// Perform a HTTP request to a specified endpoint and declare a callback block [self performRequestToEndpoint:@"endpoint" completion:^(HTTPResponse *response) { NSLog(@"Completed with response: %@", response); }]; // A helper function to avoid having to pass around too many parameters - (void)performRequestWithEndpoint:(NSString *)endpoint completion:(void (^)(HTTPResponse *response))completionBlock { // Make our HTTP request and callback our original completion block when done [self requestWithMethod:@"GET" path:endpoint completion:^(HTTPResponse *response) { if(![response error]) { // Call our original completion block completionBlock(response); } ]; } 

When the callback block is assigned by the requestWithMethod: method: path: completion: it is copied like this:

 @property (nonatomic, copy) void (^operationCompletionBlock)(HTTPResponse *response); 

Here's the crash point:

 - (void)callCompletionBlockWithResponse:(id)response { if(self.operationCompletionBlock && !self.isCancelled) { self.operationCompletionBlock(response); // crashes here (intermittently) } [self finish]; } 

The following is a stack trace:

 * thread #1: tid = 0x2403, 0x0000000000000000, stop reason = EXC_BAD_ACCESS (code=1, address=0x0) frame #0: 0x0000000000000000 frame #1: 0x00007f946b53ed01 frame #2: 0x0000000102da7cf7 Project`-[HTTPRequest callCompletionBlockWithResponse:] + 215 at HTTPRequest.m:402 frame #3: 0x0000000102da79e7 Project`__44-[HTTPRequest connectionDidFinishLoading:]_block_invoke_0 + 423 at HTTPRequest.m:381 frame #4: 0x00007fff956fea86 libdispatch.dylib`_dispatch_call_block_and_release + 18 frame #5: 0x00007fff957008f6 libdispatch.dylib`_dispatch_main_queue_callback_4CF + 308 frame #6: 0x00007fff8f07ce7c CoreFoundation`__CFRunLoopRun + 1724 frame #7: 0x00007fff8f07c486 CoreFoundation`CFRunLoopRunSpecific + 230 frame #8: 0x00007fff94f1a4d3 HIToolbox`RunCurrentEventLoopInMode + 277 frame #9: 0x00007fff94f21781 HIToolbox`ReceiveNextEventCommon + 355 frame #10: 0x00007fff94f2160e HIToolbox`BlockUntilNextEventMatchingListInMode + 62 frame #11: 0x00000001032a6e31 AppKit`_DPSNextEvent + 659 frame #12: 0x00000001032a6735 AppKit`-[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 135 frame #13: 0x00000001032a3071 AppKit`-[NSApplication run] + 470 frame #14: 0x000000010351f244 AppKit`NSApplicationMain + 867 frame #15: 0x0000000102d69512 Project`main + 34 at main.m:13 frame #16: 0x0000000102d694e4 Project`start + 52 
+6
source share
1 answer

This is a shot in the dark. You have two completion blocks, only one of which you are explicitly copying (using the property). My mental model says that completionBlock passed to performRequestWithEndpoint:completion: should be fixed in the area of โ€‹โ€‹the block that you are passing. But I know some paranoid people who can try this:

 - (void)performRequestWithEndpoint:(NSString *)endpoint completion:(void (^)(HTTPResponse *response))completionBlock { void (^copiedBlock)(HTTPResponse *response) = [completionBlock copy]; [self requestWithMethod:@"GET" path:endpoint completion:^(HTTPResponse *response) { if(![response error] && copiedBlock) { copiedBlock(response); } ]; } 
+3
source

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


All Articles