2012-03-05 10 views
13

Estoy intentando cancelar y luego lanzar un temporizador suspendido, pero cuando invoco 'dispatch_release' en él, inmediatamente obtengo EXC_BAD_INSTRUCTION.dispatch_source_cancel en un temporizador suspendido provoca EXC_BAD_INSTRUCTION

¿No es este un conjunto de acciones válido para tomar en un temporizador?

creación temporizador & suspensión:

@interface SomeClass: NSObject { } 
@property (nonatomic, assign) dispatch_source_t    timer; 
@end 

// Class implementation 
@implementation SomeClass 

@synthesize timer = _timer; 

- (void)startTimer 
{ 
    dispatch_queue_t globalQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 
            0, 0, globalQ); 

    dispatch_time_t startWhen = dispatch_walltime(DISPATCH_TIME_NOW, NSEC_PER_SEC * 1); 
    dispatch_source_set_timer(_timer, startWhen, 1 * NSEC_PER_SEC, 5000ull); 

    dispatch_source_set_event_handler(_timer, ^{ 
     // Perform a task 

     // If a particular amount of time has elapsed, kill this timer 
     if (timeConstraintReached) 
     { 
      // Can I suspend this timer within it's own event handler block? 
      dispatch_suspend(_timer); 
     } 
    }); 

    dispatch_resume(_timer); 
} 

- (void)resetTimer 
{ 
    dispatch_suspend(_timer); 

    dispatch_source_cancel(_timer); 

    // dispatch_release causes 
    // 'EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) 
    dispatch_release(_timer); 

    self.timer = nil;  
} 
@end 

Además, puedo invocar dispatch_suspend dentro del bloque event_handler de una fuente temporizador?

Cualquier ayuda sería apreciada.

Respuesta

24

La razón es que se bloquea debido a this code:

void 
_dispatch_source_xref_release(dispatch_source_t ds) 
{ 
    if (slowpath(DISPATCH_OBJECT_SUSPENDED(ds))) { 
     // Arguments for and against this assert are within 6705399 
     DISPATCH_CLIENT_CRASH("Release of a suspended object"); 
    } 
    _dispatch_wakeup(ds); 
    _dispatch_release(ds); 
} 

Por lo tanto, no se puede liberar una dispatch_source_t que ha sido suspendido. Probablemente quiera simplemente no suspenderlo en resetTimer, supongo.

Si bien no puedo encontrar nada en los documentos de por qué lo han escrito así (y el comentario alude a los pros y los contras en un radar que nunca veremos), todo lo que puedo hacer es consultar la docs where it says:

puede suspender y reanudar la entrega de eventos de origen despacho utilizando temporalmente el dispatch_suspend y dispatch_resume métodos. Estos métodos incrementan y disminuyen el conteo de suspensión para su objeto de expedición . Como resultado, debe equilibrar cada llamada al dispatch_suspend con una llamada correspondiente a dispatch_resume antes de que se reanude el evento .

Mientras eso no dice que no se puede liberar una fuente de despacho que ha sido suspendido, no digo que hay que equilibrar cada llamada, así que estoy asumiendo que es algo en la línea de que está utilizando un semáforo despachar con arreglo -el capó que tiene que ser balanced before they can be released. Eso solo es mi adivinación :-).

En cuanto a "puedo invocar dispatch_suspend dentro del bloque event_handler de una fuente de temporizador". Estoy bastante seguro de que puede, sí, según los documentos para dispatch_suspend:

La suspensión se produce después de la finalización de los bloques que se ejecutan en el momento de la llamada.

+0

Ah, ya veo. Como cada dispatch_suspend 'incrementa el recuento de suspensiones del objeto', tiene que haber un 'dispatch_resume' para equilibrar la suspensión. –

+0

¿Es factible cancelar o suspender el temporizador dentro de su bloque event_handler o tiene que suceder fuera del bloque? –

+0

Correcto, sí. Ver mi respuesta actualizada. Si bien los documentos en realidad no indican (que puedo encontrar) que no puedes liberar cuando se suspende, sospecho que tiene algún estado interno que causa maldad si lo haces. – mattjgalloway