2012-03-05 12 views
13

Sto tentando di annullare e rilasciare un timer sospeso, ma quando invoco 'dispatch_release' su di esso, ottengo immediatamente EXC_BAD_INSTRUCTION.dispatch_source_cancel su un timer sospeso causa EXC_BAD_INSTRUCTION

Non è un insieme valido di azioni da intraprendere su un timer?

creazione Timer & sospensione:

@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 

Inoltre, posso richiamare dispatch_suspend all'interno del blocco event_handler di una fonte di timer?

Qualsiasi aiuto sarebbe apprezzato.

risposta

24

Il motivo si blocca è a causa di 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); 
} 

Quindi, non è possibile rilasciare un dispatch_source_t che è stata sospesa. Probabilmente vuoi semplicemente non sospenderlo in resetTimer credo.

Mentre non riesco a trovare nulla nei documenti per il motivo per cui l'hanno scritto in questo modo (e il commento allude ai pro e ai contro che si trovano in un radar che non vedremo mai), tutto ciò che posso fare è fare riferimento a il docs where it says:

È possibile sospendere e riprendere l'erogazione di eventi di origine spedizione temporaneamente utilizzando l'dispatch_suspend e dispatch_resume metodi. Questi metodi incrementano e decrementano il conteggio di sospensione per l'oggetto di spedizione . Di conseguenza, è necessario bilanciare ogni chiamata a dispatch_suspend con una chiamata corrispondente a dispatch_resume prima che riprenda la consegna dell'evento .

Pur che non dice, non è possibile rilasciare una fonte di spedizione che è stata sospesa, non dire che è necessario bilanciare ogni chiamata in modo da sto supponendo che è qualcosa sulla falsariga di esso sta usando un semaforo della spedizione, sotto -il cappuccio che deve essere balanced before they can be released. Questa è solo la mia ipotesi però :-).

Per quanto riguarda "posso richiamare dispatch_suspend all'interno del blocco event_handler di una sorgente di timer". Sono abbastanza sicuro che si può, sì, secondo la documentazione per dispatch_suspend:

La sospensione si verifica dopo il completamento di tutti i blocchi in esecuzione al momento della chiamata.

+0

Ah capisco. Poiché ogni dispatch_suspend 'incrementa il conteggio delle sospensioni dell'oggetto', ci deve essere un 'dispatch_resume' per bilanciare la sospensione. –

+0

È possibile annullare o sospendere il timer all'interno del relativo blocco event_handler o deve avvenire al di fuori del blocco? –

+0

Proprio così. Vedi la mia risposta aggiornata. Sebbene i documenti non dichiarino effettivamente (che posso trovare) che non puoi rilasciare quando è sospeso, sospetto che abbia qualche stato interno che causa cattiveria se lo fai. – mattjgalloway