2012-07-25 18 views
6

Ho una sottoclasse NSOperation personalizzata che utilizzo per effettuare richieste HTTP. Accetta un callback basato su blocchi che viene eseguito al termine di NSOperation. Tutto funziona di conseguenza, ma ho riscontrato uno strano arresto intermittente durante il tentativo di eseguire la chiamata di completamento. Ho letto un sacco di problemi EXEC_BAD_ACCESS basati su blocchi causati non copiando correttamente un blocco quando lo si passa a metodi aggiuntivi.Blocco richiamata in arresto anomalo con EXC_BAD_ACCESS

Credo che il mio problema riguardi il modo in cui utilizzo i blocchi. Includerò un caso d'uso standard per la mia domanda qui sotto. La radice del mio problema è probabilmente dovuta a un equivoco di proprietà in cui sono coinvolti i blocchi.

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

Quando un blocco di richiamata viene assegnato tramite il requestWithMethod: percorso: il completamento: il metodo, viene copiato in questo modo:

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

Ecco il punto dello schianto:

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

    [self finish]; 
} 

Attached sotto è la traccia dello stack:

* 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

si sta effettivamente utilizzando la proprietà per impostarlo, giusto? per esempio. 'self.operationCompletionBlock = completionBlock;' non lo imposta direttamente sulla variabile di istanza? per esempio. 'operationCompletionBlock = completionBlock;' – newacct

+0

Sì! È impostato esattamente come hai descritto 'self.operationCompletionBlock = completionBlock;' – ndg

+0

Impossibile vedere tutto ciò che è sbagliato. Forse dovresti mostrare la richiestaWithMethod: percorso: completamento: metodo – newacct

risposta

3

Questo è uno sparo al buio. Hai due blocchi di completamento, solo uno dei quali copi esplicitamente (usando la proprietà). Il mio modello mentale dice che lo completionBlock passato a performRequestWithEndpoint:completion:dovrebbe essere catturato nell'ambito del blocco che si passa. Ma conosco alcuni paranoici che potrebbero provare questo:

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

questo ha funzionato per me piuttosto bene, solo una cosa è un refuso nel codice - invece di 'void (^ copyBlock) (risposta HTTPResponse *))' dovrebbe essere 'void (^ copyBlock) (Risposta HTTPResponse *), grazie! –

+0

Correzione sintassi valida, grazie. La grammatica originale è migliore, IMO. –

Problemi correlati