Objective c EXC\u访问错误时块回调崩溃
我有一个自定义的NSOperation子类,用于发出HTTP请求。它接受基于块的回调,该回调在NSO操作完成时执行。一切都会相应地工作,但在尝试执行完成回调时,我遇到了一个奇怪的间歇性崩溃。我读过很多基于块的EXEC\u BAD\u访问问题是由于在将块传递给其他方法时没有正确复制该块而导致的 我相信我的问题与我如何利用积木有关。我将在下面为我的应用程序提供一个标准用例。我的问题的根源很可能是对区块所有权的误解Objective c EXC\u访问错误时块回调崩溃,objective-c,cocoa,objective-c-blocks,Objective C,Cocoa,Objective C Blocks,我有一个自定义的NSOperation子类,用于发出HTTP请求。它接受基于块的回调,该回调在NSO操作完成时执行。一切都会相应地工作,但在尝试执行完成回调时,我遇到了一个奇怪的间歇性崩溃。我读过很多基于块的EXEC\u BAD\u访问问题是由于在将块传递给其他方法时没有正确复制该块而导致的 我相信我的问题与我如何利用积木有关。我将在下面为我的应用程序提供一个标准用例。我的问题的根源很可能是对区块所有权的误解 // Perform a HTTP request to a specified en
// 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);
}
];
}
当通过requestWithMethod:path:completion:method分配回调块时,它的复制方式如下:
@property (nonatomic, copy) void (^operationCompletionBlock)(HTTPResponse *response);
以下是这次事故的要点:
- (void)callCompletionBlockWithResponse:(id)response
{
if(self.operationCompletionBlock && !self.isCancelled)
{
self.operationCompletionBlock(response); // crashes here (intermittently)
}
[self finish];
}
下面是堆栈跟踪:
* 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
这是在黑暗中拍摄的。您有两个完成块,仅显式复制其中一个(使用属性)。我的心智模型说,传递给
performRequestWithEndpoint:completion:
的completionBlock
应该在传递的块的范围内捕获。但我知道有些偏执狂可能会这样做:
- (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);
}
];
}
您实际上是在使用属性来设置它,对吗?e、 g.
self.operationCompletionBlock=completionBlock代码>不直接在实例变量上设置它?e、 g.operationCompletionBlock=completionBlock代码>是的!它的设置与您描述的完全相同self.operationCompletionBlock=completionBlock代码>看不到任何错误。也许您应该显示requestWithMethod:path:completion:method您确定错误访问位于块本身,而不是块内部吗?块体就是您在上面粘贴的NSLog语句吗?凭直觉,我将completionCallback封装在一个dispatch_async中,它通过dispatch_get_main_queue()运行,这似乎可以清除崩溃。如前所述,我认为这可能是一个所有权问题,因为我引用的是self,并且块中有块。通过在主队列上执行回调,我能够避免崩溃。但如果有人能明确解释为什么会这样,我会感兴趣的。否则,我将添加此作为答案。这对我来说非常有效,唯一的问题是代码中的输入错误-而不是void(^copiedBlock)(HTTPResponse*response))
应该是void(^copiedBlock)(HTTPResponse*response)
,谢谢!语法更正有效,谢谢。依我看,原来的语法比较好。