2011-12-21 9 views
14

Ho un oggetto NSRunLoop, a cui aggiungo timer e flussi. Funziona alla grande. Fermarlo è un'altra storia in tutto e per tutto.CFRunLoopRun() vs [NSRunLoop run]

Corro il ciclo utilizzando [runLoop run].

Se provo a interrompere il ciclo utilizzando CRunLoopStop([[NSRunLoop currentRunLoop] getCFRunLoop]), il ciclo non si interromperà. Se avvio il ciclo usando invece CRunLoopRun(), funziona. Mi sono anche assicurato che la chiamata sia fatta sul thread corretto (quello che esegue il mio ciclo di esecuzione personalizzato). Ho effettuato il debug di questo con pthread_self().

Ho trovato un archivio di mailing list, in cui uno sviluppatore ha detto "non preoccupatevi di usare CRunLoopStop() se avete iniziato il ciclo usando il metodo di esecuzione di NSRunLoop". Posso capire perché è così com'è: di solito si accoppiano inizializzatori e finalizzatori dello stesso insieme di funzioni.

Come si ferma uno NSRunLoop senza "ricorrere a CF"? Non vedo un metodo stop su NSRunLoop. La documentazione dice che ci si può fermare un ciclo corsa in tre modi:

  1. Configurare il ciclo di esecuzione per l'esecuzione con un valore di timeout
  2. Dillo ciclo corsa di smettere di usare CFRunLoopStop()
  3. Rimuovere tutte le sorgenti di ingresso, ma questo è un modo affidabile per fermare il ciclo di periodo, perché si può mai sapere che cosa bloccato quello che nel circuito corsa dietro la schiena

Beh, ho già provato 2. e non c'è un "brutto" si sentono ad essa, perché si scavare nella CF. 3. è fuori questione - non mi piace il codice non deterministico.

Questo ci lascia 1. Se capisco correttamente i documenti, non è possibile "aggiungere" un timeout a un ciclo di esecuzione già esistente. È possibile eseguire solo nuovi cicli di esecuzione con un timeout. Se eseguo un nuovo ciclo di esecuzione, non risolverà il mio problema, poiché creerà solo un ciclo di esecuzione annidato. Continuerò a tornare al vecchio, lo stesso che volevo fermare ... giusto? Potrei aver frainteso questo. Inoltre, non voglio eseguire il ciclo con un valore di timeout. Se lo faccio, dovrò fare un compromesso tra la masterizzazione dei cicli della CPU (basso valore di timeout) e la reattività (alto valore di timeout).

Questa è la configurazione che ho in questo momento (pseudo codice-ish):

Communicator.h

@interface Communicator : NSObject { 
    NSThread* commThread; 
} 

-(void) start; 
-(void) stop; 
@end 

Communicator.m

@interface Communicator (private) 
-(void) threadLoop:(id) argument; 
-(void) stopThread; 
@end 

@implementation Communicator 
-(void) start { 
    thread = [[NSThread alloc] initWithTarget:self 
            selector:@selector(threadLoop:) 
             object:nil]; 
    [thread start]; 
} 

-(void) stop { 
    [self performSelector:@selector(stopThread) 
       onThread:thread 
       withObject:self 
      waitUntilDone:NO]; 
    // Code ommitted for waiting for the thread to exit... 
    [thread release]; 
    thread = nil; 
} 
@end 

@implementation Communicator (private) 
-(void) stopThread { 
    CRunLoopStop([[NSRunLoop currentRunLoop] getCFRunLoop]); 
} 

-(void) threadLoop:(id) argument { 
    // Code ommitted for setting up auto release pool 

    NSRunLoop* runLoop = [NSRunLoop currentRunLoop]; 

    // Code omitted for adding input sources to the run loop 

    CFRunLoopRun(); 
    // [runLoop run]; <- not stoppable with 

    // Code omitted for draining auto release pools 

    // Code omitted for signalling that the thread has exited 
} 
@endif 

Che cosa sono io fare? È comune/un buon modello per scherzare con CF? Non conosco abbastanza bene la Fondazione. L'interferenza nello strato CF può essere pericolosa (rispetto alla corruzione della memoria, incoerenze, perdite di memoria)? C'è uno schema migliore per ottenere ciò che sto cercando di ottenere?

risposta

7

Stai andando bene. Non c'è problema nell'usare CoreFoundation quando non puoi raggiungere il tuo obiettivo con Foundation. Poiché CoreFoundation è C, è più facile incasinarsi con la gestione della memoria, ma non vi è alcun pericolo intrinseco nell'uso di CFRunLoop anziché NSRunLoop (sometimes it may even be safer: CFRunLoop API sono thread-safe mentre NSRunLoop non lo è).

Se si desidera interrompere NSRunLoop, è possibile eseguirlo utilizzando runMode:beforeDate:. runMode:beforeDate: restituisce non appena viene elaborata una sorgente di input, quindi non è necessario attendere fino al raggiungimento della data di timeout.

NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; 
NSDate *date = [NSDate distantFuture]; 
while (!runLoopIsStopped && [runLoop runMode:NSDefaultRunLoopMode beforeDate:date]); 

Poi, per fermare il ciclo di esecuzione, è sufficiente impostare runLoopIsStopped-YES.

+0

Ah, intelligente! Non ho visto che 'runMode' ha funzionato solo una volta. C'è una significativa penalità per le prestazioni subita dall'inizio del ciclo di corse più e più volte? –

+0

No, non c'è: ecco come funzionano '- [NSRunLoop run]' e '- [NSRunLoop runUntilDate:]'. –

+1

Il thread non si fermerà se si imposta solo runLoopIsStopped su YES. È necessario impostarlo su SÌ ed eseguire un'azione sul thread di quel runloop, altrimenti [runLoop runMode: NSDefaultRunLoopMode beforeDate: date] non verrà chiuso. Mi ci sono voluti anni per capire. – Pada