2012-07-25 15 views
6

Ich habe eine benutzerdefinierte NSOperation-Unterklasse, die ich für HTTP-Anfragen verwende. Es akzeptiert einen blockbasierten Rückruf, der ausgeführt wird, wenn die NSOperation abgeschlossen ist. Alles funktioniert entsprechend, aber ich erlebe einen seltsamen, zeitweiligen Absturz, wenn ich versuche, meinen Completion-Callback auszuführen. Ich habe viele blockbasierte EXEC_BAD_ACCESS-Probleme gelesen, die dadurch verursacht werden, dass ein Block nicht korrekt kopiert wird, wenn er an zusätzliche Methoden übergeben wird.Block-Callback stürzt mit EXC_BAD_ACCESS ab

Ich glaube mein Problem bezieht sich darauf, wie ich Blöcke benutze. Ich werde im Folgenden einen Standardanwendungsfall für meine Anwendung angeben. Die Ursache meines Problems liegt wahrscheinlich in einem Missverständnis der Eigentümer, wenn es um Blöcke geht.

// 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); 
     } 
    ]; 
} 

Wenn ein Callback-Block über den requestWithMethod zugeordnet: Pfad: Fertigstellung:

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

Hier ist der Punkt des Absturzes: Methode ist es wie so kopiert

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

    [self finish]; 
} 

Befestigt Unten ist die Stapelverfolgung:

* 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

Sie verwenden tatsächlich die Eigenschaft, um es zu setzen, richtig? z.B. 'self.operationCompletionBlock = completionBlock;' setze es nicht direkt auf die Instanzvariable? z.B. 'operationCompletionBlock = completionBlock;' – newacct

+0

Ja! Es ist genau so eingestellt, wie Sie beschrieben haben: self.operationCompletionBlock = completionBlock; ' – ndg

+0

Kann nichts sehen, was falsch ist. Vielleicht sollten Sie das RequestWithMethod zeigen: Pfad: Vervollständigung: Methode – newacct

Antwort

3

Dies ist eine Aufnahme im Dunkeln. Sie haben zwei Abschlussblöcke, von denen Sie nur einen explizit kopieren (mit der Eigenschaft). Mein geistiges Modell sagt, dass die completionBlock, die an performRequestWithEndpoint:completion:übergeben wird, im Rahmen des Blocks gefangen sein sollte, den Sie weitergeben. Aber ich kenne einige paranoide Leute, die das versuchen könnten:

- (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

einen Completion Block zuweisen Das funktionierte für mich ziemlich gut, nur eine Sache ist ein Tippfehler im Code - anstelle von 'void (^ copiedBlock) (HTTPResponse * Antwort))' sollte 'void (^ copyBlock) (HTTPResponse * Antwort) ', danke! –

+0

Syntaxkorrektur gültig, danke. Die ursprüngliche Grammatik ist besser, IMO. –