2011-02-07 24 views
9

E 'generalmente una buona idea di chiamare -[NSRunLoop runUntilDate:]? Sembra funzionare senza problemi, ma mi rende nervoso dire al ciclo di esecuzione di essere eseguito all'interno del ciclo di esecuzione.Sta chiamando - [NSRunLoop runUntilDate:] una buona idea?

Maggiori informazioni:

Ho un progetto in questo momento che è il recupero dei dati da un servizio REST. Un'unica informazione fondamentale che è necessario ottenere è l'intervallo di date con dati validi. E 'un po' piccolo di dati che ha solo bisogno di essere diventato una volta, così ho deciso che il modo migliore per gestire la cosa è di avere la proprietà scaricare i dati se la variabile locale è nil. Sto usando ASIHTTPRequest e un ASINetworkQueue, quindi tutto è asincrono per impostazione predefinita e in ordine per far funzionare tutto questo, questa proprietà non posso tornare fino a quando i dati sono stati scaricati ed elaborati. Ecco un profilo del mio codice, i nomi delle variabili sono stati cambiati per proteggere gli innocenti:

__block BOOL isWorking = YES; 
__block ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:/*actual URL*/] autorelease]; 
[request setCompletionBlock:^{ 
    // set local variable 
    isWorking = NO; 
}]; 
[request setFailedBlock:^{ 
    // show alert to user 
    isWorking = NO; 
}]; 
[queue addOperation:request]; 

while (isWorking) { 
    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 
} 

Ancora una volta, questo sembra funzionare bene. Ci sono potenziali problemi con l'utilizzo di questo approccio?

risposta

3

si deve fare attenzione a non fare questo da qualsiasi metodo che potrebbe essere chiamato dal ciclo corsa si invoca, a meno che l'albero chiamata sovrapposizione è completamente re-entrant.

Il codice UI di Cocoa Touch non è documentato come rientranti (in effetti, ci sono avvertimenti/suggerimenti da Apple DTS che non lo è), quindi se il gestore di dati di recupero può essere in qualsiasi modo chiamato da un metodo dell'interfaccia utente (o altro codice non rientranti che potrebbe essere chiamato nel ciclo di esecuzione dell'interfaccia utente), non è consigliabile chiamare il ciclo di esecuzione dell'interfaccia utente dall'interno.

+0

Quindi, in questo caso, sembra che questo approccio è sicuro dal momento che non c'è niente altro che potrebbe chiamare questa proprietà mentre è impegnata a scaricare i dati. Ma se ci fosse una situazione in cui l'utente potrebbe premere un pulsante mentre era occupato e richiamare di nuovo quella proprietà, allora esploderebbe? Buono a sapersi. –

+0

Questa proprietà viene utilizzata direttamente o indirettamente da qualsiasi metodo o delegato dell'interfaccia utente? Se è così, è ancora un grosso problema. – hotpaw2

+0

Sono certo sarà solo colpire questo codice una volta quando l'applicazione carica inizialmente prima che l'interfaccia utente è ancora presentato all'utente. Dopodiché, i dati saranno lì e useranno la variabile locale. Se non riesce a scaricare i dati per qualche motivo, visualizza un avviso modale per l'utente e non possono accedere all'app fino a quando non viene scaricata correttamente. –

4

Non è meglio per visualizzare una sorta di un filatore e strappare giù in risposta agli eventi di completamento asincrono dal codice di rete? Ad esempio:

[self displayLoadingSpinner]; 
[request setCompletionBlock:^{ 
    [self handleSuccess]; 
    dispatch_async(dispatch_get_main_queue(), ^{ 
     [self hideLoadingSpinner]; 
    }]; 
}]; 
[request setFailedBlock:^{ 
    [self handleFailure]; 
    dispatch_async(dispatch_get_main_queue(), ^{ 
     [self hideLoadingSpinner]; 
    }]; 
}]; 
[queue addOperation:request]; 

Lo considererei meglio della monkeking con il ciclo di esecuzione. Ma potrebbe essere che tu lo sappia già e vuoi solo sapere quali sono esattamente gli inconvenienti presenti nella soluzione runloop?


Se si desidera bloccare fino a quando il valore è pronto, è possibile utilizzare un semaforo:

dispatch_semaphore_t sem = dispatch_semaphore_create(0); 
[request setCompletionBlock:^{ 
    dispatch_semaphore_signal(sem); 
}]; 
[queue addOperation:request]; 

dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); 
dispatch_release(sem); 
+0

Questo è l'approccio che adotterei se fosse possibile un approccio al metodo delegato/callback. In questo caso, ho bisogno che il valore venga impostato prima di tornare dalla proprietà altrimenti l'applicazione continuerà con dati non validi. Potrei refactoring il codice in modo che il modello di progettazione avrebbe funzionato. Questa domanda è davvero di scoprire se ci potrebbero essere problemi inaspettati con il mio approccio che non vedo ancora. –

+0

L'approccio del semaforo non avrebbe bloccato il thread principale? Non impedirebbe l'esecuzione dei blocchi? –

+0

Dipende da come viene implementata la libreria HTTP che si utilizza.Sì, il thread principale si bloccherà, così se la libreria usa il thread principale, non puoi usare il semaforo. – zoul

Problemi correlati