2012-07-25 65 views
6

我有一个自定义NSOperation子类,用于发出HTTP请求。它接受在NSOperation完成时执行的基于块的回调。一切正常,但我在尝试执行完成回调时遇到了一个奇怪的间歇性崩溃。我读过很多基于块的EXEC_BAD_ACCESS问题是由于在将其传递给其他方法时未正确复制块而导致的。使用EXC_BAD_ACCESS阻止回调崩溃

我相信我的问题与我如何使用块有关。我将在下面为我的应用程序提供一个标准用例。我的问题的根源很可能归因于所有权方面的所有权误解。

// 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分配:路径:完成:方法,它被复制像这样:

@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 
+0

你实际上是使用属性来设置它,对吗?例如'self.operationCompletionBlock = completionBlock;'不直接在实例变量上设置它?例如'operationCompletionBlock = completionBlock;' – newacct 2012-07-25 09:29:27

+0

是的!它的设置与您所描述的完全相同'self.operationCompletionBlock = completionBlock;' – ndg 2012-07-25 09:47:31

+0

看不到任何错误。也许你应该显示requestWithMethod:path:completion:方法 – newacct 2012-07-25 20:52:09

回答

3

这是在黑暗中拍摄的照片。您有两个完成块,其中只有一个完全复制(使用属性)。我的心智模式说completionBlock传递给performRequestWithEndpoint:completion:应该是捕获在您传递的块的范围内。但我知道一些偏执的人可能会试试这个:

- (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); 
     } 
    ]; 
}  
+1

这对我很好,唯一的东西是在代码中的错字 - 而不是'void(^ copiedBlock)(HTTPResponse *响应))'应该是'void(^ copiedBlock )(HTTPResponse *响应)',谢谢! – 2014-08-08 18:06:11

+0

语法更正有效,谢谢。原文语法比较好,国际海事组织。 – 2016-03-01 14:53:37

相关问题