2013-07-04 16 views
34

Ho un metodo che dovrebbe supportare essere chiamato da qualsiasi coda e dovrebbe aspettarsi di farlo. Esegue un codice in uno stesso thread in background e quindi usa dispatch_get_main_queue quando restituisce un valore al suo argomento block. Non voglio forzarlo sulla coda principale se non lo era quando è entrato nel metodo. C'è un modo per ottenere un puntatore alla coda di invio corrente?Ricevi la coda di invio corrente?

risposta

20

Si ha la possibilità di "dispatch_get_current_queue()", tuttavia l'SDK iOS 6.1 definisce questa API con queste disclaimer:

"Recommended for debugging and logging purposes only:"

e

"This function is deprecated and will be removed in a future release.".

Here's another related question with some alternatives potete considerare se volete un codice che sia a prova di futuro.

+2

Non è deprecato? –

+1

hai ragione, Martin! Riformerò la mia risposta un po '. –

25

Con il ritiro di dispatch_get_current_queue() non esiste alcun modo per sapere quale coda si sta eseguendo. Se si consulta lo GCD sources, si noterà che ciò è dovuto al fatto che potrebbero esserci più risposte alla domanda "a quale coda si sta eseguendo?" (Perché le code alla fine prendono di mira una delle code globali, ecc.)

Se si desidera garantire che un blocco futuro venga eseguito su una coda specifica, l'unico modo è di rendere l'API accetta una coda come parametro lungo con il blocco di completamento. Ciò consente al chiamante di decidere dove eseguire il completamento.

Se semplicemente sapere se il chiamante è sul thread principale o meno è sufficiente, è possibile utilizzare +[NSThread isMainThread] per scoprirlo. Nel caso comune, tutti i blocchi in esecuzione nella coda GCD principale verranno eseguiti sul thread principale. (Un'eccezione a questa regola è se l'applicazione utilizza dispatch_main() al posto di un ciclo di esecuzione principale, sarà necessario utilizzare dispatch_get_specific e gli amici per rilevare con certezza che si sta eseguendo sulla coda principale - questa è una circostanza relativamente rara.) Più comunemente, si noti che non tutto il codice che viene eseguito sul thread principale viene eseguito sulla coda principale tramite GCD; GCD è subordinato al thread runloop principale. Per il tuo caso specifico sembra che potrebbe essere sufficiente.

17

È possibile utilizzare NSOperationQueue per questo. NSOperationQueue ha la funzione di classe [NSOperationQueue currentQueue], che restituisce la coda corrente come oggetto NSOperationQueue. Per ottenere l'oggetto della coda di spedizione è possibile utilizzare [NSOperationQueue currentQueue].underlyingQueue, che restituisce la coda corrente come dispatch_queue_t.

Swift 3:

if let currentDispatch = OperationQueue.current?.underlyingQueue { 
    print(currentDispatch) 
} 

- lavora per la coda principale!

+0

Si noti che underlyingQueue è stato aggiunto in iOS 8.0. –

+8

Ho giocato con questo, e non sembra che otterrai il valore di ritorno corretto per '[NSOperationQueue currentQueue]' se il codice è in esecuzione in una coda GCD non associata a un 'NSOperationQueue'. In altre parole, se eseguo direttamente un blocco in una coda GCD e chiamo '[NSOperationQueue currentQueue] .underlyingQueue' da quel blocco, non ottengo mai lo stesso valore della coda effettiva in cui eseguo il blocco. – emaloney

+2

Penso che @emaloney abbia ragione. Ho fatto un esperimento che eseguendo un blocco task gcd con 'dispatch_sync (myqueue,^{})' e '[NSOperationQueue currentQueue]' restituisce la coda principale. –

10

Con la deprecazione di dispatch_get_current_queue() non si può ottenere direttamente un puntatore alla coda si sta lavorando su, ma si fa può ottenere l'etichetta della coda corrente chiamando dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL) e che ti dà una certa flessibilità.

È sempre possibile verificare se si è su quella coda specifica confrontando le proprie etichette, quindi nel caso in cui non si desideri forzarlo sulla coda principale, quando si è immesso il metodo è sufficiente utilizzare il seguente flag :

let isOnMainQueue = (dispatch_queue_get_label(dispatch_get_main_queue()) == dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)) 

Se si esegue sulla coda globale, si rispettosamente ottenere l'etichetta della coda associata con il suo tipo QOS, che può essere uno dei seguenti:

com.apple.root.user-interactive-qos //qos_class_t(rawValue: 33) 
com.apple.root.user-initiated-qos //qos_class_t(rawValue: 25) 
com.apple.root.default-qos   //qos_class_t(rawValue: 21) 
com.apple.root.utility-qos   //qos_class_t(rawValue: 17) 
com.apple.root.background-qos  //qos_class_t(rawValue: 9) 

e poi si può usare dispatch_get_global_queue(qos_class_self(), 0) che ti restituirà quello stesso g coda lobale su cui stai correndo.

Ma credo che Apple ci scoraggi in particolare dal delimitare la logica alla coda che abbiamo ricevuto, quindi è meglio utilizzarla per scopi esclusivamente di debug.

+0

Ho trovato questo metodo non affidabile. Secondo il documento, l'etichetta è un parametro opzionale, quindi può essere NULL. dispatch_queue_get_label() restituisce una stringa vuota se non è stata fornita alcuna etichetta durante la creazione. – soflare

+0

è vero, è solo una soluzione, come ho detto - per il test e il debug potrebbe essere particolarmente utile, ma legare la logica nel codice non è una buona idea .. – ambientlight

+0

Dal momento che la soluzione sembra il metodo più noto, ho usarlo per determinare se la coda corrente è una specifica coda seriale. Se sì, basta chiamare direttamente i blocchi invece di chiamare dispatch_sync che provoca il dead lock. Per evitare il caso d'angolo che ho menzionato prima. Semplicemente rifiuto ogni coda senza etichetta. – soflare

1

Come approccio alternativo a questo metodo NSOBject, performSelector:withObject:afterDelay: invia la chiamata al ciclo di esecuzione del thread corrente. Secondo la documentazione:

Questo metodo imposta un timer per eseguire il messaggio aSelector sul ciclo di esecuzione del thread corrente .

Ovviamente sto suggerendo che utilizza questo con un ritardo pari a zero, che, secondo la documentazione ancora:

Impostazione di un ritardo di 0 non necessariamente provoca il selettore da eseguita immediatamente. Il selettore è ancora in coda sul ciclo di esecuzione del thread e viene eseguito il prima possibile.

Sfortunatamente richiede un solo argomento, quindi potrebbero essere necessari alcuni accorgimenti se il metodo richiede più o meno.

Un'altra cosa che ho notato è che questo metodo non è disponibile per i protocolli, ma solo per le implementazioni. Ciò è dovuto a questo metodo che vive in una categoria NSObject e non nell'interfaccia NSObject (vedi PS sotto). Questo può essere facilmente risolto mediante casting su id.

PS: esistono due diversi NSObject, un protocollo e un'implementazione. Avviso NSObject dichiarazione:

@interface NSObject <NSObject> { ... } 

Può sembrare strano, ma uno viene dichiarato (dopo @interface) e l'altro è un protocollo precedentemente dichiarato (tra < e >). Quando si dichiara un protocollo che estende NSObject (cioè, @protocol Foo <NSObject>) il protocollo eredita i metodi dal successivo, ma non dal primo. Alla fine il protocollo viene implementato da una classe che eredita dall'implementazione NSObject, quindi tutte le istanze che ereditano dall'implementazione NSObject sono ancora valide. Ma sto andando fuori tema.

1

Esiste ancora un modo per confrontare la coda.

Quando si imposta la coda, assicurarsi di aggiungere l'etichetta. Per i miei scopi, ho una coda condivisa che viene utilizzata per accedere a un database per impedire il blocco del database. Nel mio DB.file di m Ho definito la funzione di coda condivisa come:

const char *kTransactionQueueLabel = "DB_TRANSACTION_DISPATCH_QUEUE"; 

+ (dispatch_queue_t)sharedDBTransactionQueue { 
    static dispatch_queue_t sharedDBQueue = nil; 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 
     sharedDBQueue = dispatch_queue_create(kTransactionQueueLabel, DISPATCH_QUEUE_SERIAL); 
    }); 

    return sharedDBQueue; 
} 

La coda di transazione db condivisa viene utilizzata localmente nel file di inviare tutte le esecuzioni al database. Tuttavia, è disponibile anche un accesso pubblico per consentire l'invio di transazioni intere al database. Quindi internamente, se un metodo di accesso ai DB viene chiamato all'interno della coda delle transazioni, è necessario effettuare il dispatch internamente su una coda diversa (tutte le spedizioni sincrone). Così internamente, invio sempre sulla coda corretta usando il getter sottostante.

/** 
* @description Decide which queue to use - if we are already in a transaction, use the internal access queue, otherwise use the shared transaction queue. 
*/ 
- (dispatch_queue_t)getProperQueueForExecution { 
    const char *currentLabel = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL); 
    dispatch_queue_t sharedAccessQueue = [DB sharedDBTransactionQueue]; 
    if (strcmp(currentLabel, kTransactionQueueLabel) == 0) { 
     sharedAccessQueue = [DB sharedInternalDBAccessQueue]; 
    } 

    return sharedAccessQueue; 
} 

Speriamo che questo aiuti. Scusa per il lungo esempio. Il Gist di esso è che è possibile utilizzare

const char *currentLabel = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL); 

per ottenere l'etichetta della coda corrente e confrontarla con un'etichetta definita.

0

Ho gli stessi requisiti funzionali indicati nel post originale. Dovresti essere in grado di chiamare questa funzione asincrona su qualsiasi coda, ma se chiamata sulla coda principale, quindi richiamare l'utente sulla coda principale. Semplicemente lo gestisco in questo modo:

// cache value for if we should callback on main queue 
BOOL callbackOnMT = [NSThread isMainThread]; 

// ... 
// ... do async work... 
// ... 

if (callbackOnMT && ![NSThread isMainThread]){ 
    dispatch_async(dispatch_get_main_queue(), ^{ 
     // callback to user on main queue 
     // as they called this function on main queue 
     callbackToUser(); 
    }); 
} 
else{ 
    // callback to user on our current queue 
    // as they called this function on a non-main queue 
    callbackToUser(); 
} 
Problemi correlati