2012-07-25 26 views
6

Tengo una subclase NSOperation personalizada que utilizo para realizar solicitudes HTTP. Acepta una devolución de llamada basada en bloques que se ejecuta cuando se completa la operación NSO. Todo funciona en consecuencia, pero estoy experimentando un bloqueo extraño e intermitente cuando intento ejecutar mi devolución de llamada de finalización. He leído muchos problemas EXEC_BAD_ACCESS basados ​​en bloques causados ​​por copiar incorrectamente un bloque al pasarlo a métodos adicionales.Bloquear devoluciones de llamadas con EXC_BAD_ACCESS

Creo que mi problema se relaciona con la forma en que estoy haciendo uso de bloques. A continuación, incluiré un caso de uso estándar para mi aplicación. La raíz de mi problema probablemente se deba a un malentendido de la propiedad en lo que respecta a los bloques.

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

Cuando se asigna un bloque de devolución de llamada a través de la requestWithMethod: ruta: completado: método, se copia así:

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

aquí está el punto del accidente:

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

    [self finish]; 
} 

Attached a continuación es el rastro de la pila:

* 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

¿Estás usando la propiedad para configurarlo, verdad? p.ej. 'self.operationCompletionBlock = completionBlock;' no lo establece en la variable de instancia directamente? p.ej. 'operationCompletionBlock = completionBlock;' – newacct

+0

¡Sí! Se establece exactamente como describió 'self.operationCompletionBlock = completionBlock;' – ndg

+0

No se puede ver nada que esté mal. Tal vez debería mostrar el método requestWithMethod: path: completion: – newacct

Respuesta

3

Este es un tiro en la oscuridad. Tiene dos bloques de finalización, de los cuales solo uno copia explícitamente (utilizando la propiedad). Mi modelo mental dice que el completionBlock pasado a performRequestWithEndpoint:completion:debe ser capturado en el alcance del bloque que pasa. Pero sé que algunas personas paranoicas podrían probar esto:

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

esto funcionó bastante bien para mí, lo único es un error tipográfico en el código, en lugar de 'void (^ copyBlock) (respuesta HTTPResponse *)' debe ser 'void (^ copiadoBloque) (Respuesta HTTPResponse *) ', ¡gracias! –

+0

Corrección de sintaxis válida, gracias. La gramática original es mejor, IMO. –

Cuestiones relacionadas