2012-10-25 22 views
7

Sto usando GCD per inviare la richiesta HTTP in modo asincrono. Ecco il codice che non funziona:GCD con NSURLConnection

dispatch_async(connectionQueue, ^{ 
     NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init]; 

     [request setURL:[NSURL URLWithString:[NSString stringWithFormat:someURL]]]; 


     NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; 
     [connection start];//Not working 
    }); 

il codice sopra non funziona affatto. Non ricevo alcuna richiamata nei metodi NSURLConnectionDelegate.

ma quando ho provato il seguente codice, tutto ha funzionato bene e ho avuto callback corretto

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init]; 

[request setURL:[NSURL URLWithString:[NSString stringWithFormat:someURL]]]; 

NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; 

dispatch_async(connectionQueue, ^{ 

    [connection start]; // working fine. But WHY ???? 
}); 

qualcuno può spiegare questo strano comportamento di blocco/GCD?

+1

NSURLConnection esegue già il caricamento asincrono dei dati, quindi perché utilizzare GCD? –

+1

è Apple lo consiglia? utilizzando GCD per mantenere il blocco sopra il pezzo di codice. – Ashu

+0

Il secondo esempio ha già avviato la connessione prima del blocco dispatch_async. Se non si stabilisce che la connessione non si avvia immediatamente impostando il valore "Inizia immediatamente" su "NO", la connessione verrà avviata non appena viene creata un'istanza. In questo caso il blocco dispatch_async è ridondante. Vedere la mia risposta di seguito per un esempio di come utilizzare NSURLConnection in modo asincrono. – user298261

risposta

2

Prova questo nella prima parte del codice Sample-

dispatch_async(dispatch_get_main_queue(), ^(void){ 
    NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; 
    [connection start]; 
} 

Se si mette la connessione in una coda di fondo, si ottiene spinto via dopo la coda è completa, e quindi non si ottiene la vostra delegare le richiamate. La connessione può essere nella coda principale in modo che rimanga nel ciclo di esecuzione principale affinché si verifichino i callback. Oppure puoi creare il tuo runloop che gestisce le operazioni in background per te come suggerito da altri.

+1

La connessione non dovrebbe assolutamente essere sulla discussione principale. Vedi QA tecnico 1693. https://developer.apple.com/library/ios/#qa/qa1693/_index.html – quellish

+2

Ummm, no, QA 1693 non dice nulla del genere. –

+1

Non dovrebbe essere nella discussione principale, almeno, non se la connessione deve essere eseguita in modo veramente asincrono. Il poster deve solo imparare come accedere correttamente a RunLoop del thread in background per eseguire correttamente il polling degli eventi di connessione. – user298261

1

Un NSURLConnection eseguirà sempre il recupero dei dati sul thread su cui è stato creato (alloc init). Questo spiega perché avrebbe funzionato nel secondo modo. Il primo modo funziona ma il thread muore prima che tu possa ricevere qualsiasi informazione da NSURLConnection. NSURLConnection consente già per il download asincrono, ma se si desidera eseguire anche la gestione dei dati in modo asincrono è necessario utilizzare il seguente metodo:

+ (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler 

Ci sono alcune restrizioni che il metodo di autenticazione come è limitato e non è possibile tenere traccia gran parte del documento è stata scaricata fino ad oggi. È inoltre necessario specificare un NSOperationQueue che si crea, la coda di default è la coda del ciclo principale.

2

Quando si utilizza NSURLConnection per la comunicazione asincrona, è necessario disporre del thread in cui viene creata un'istanza per collegare la connessione al proprio RunLoop in modo da disporre del polling del thread per i metodi di delega della stessa connessione.

Il modo corretto per istanziare un NSURLConnection asincrono, senza fare affidamento sul runloop del thread principale è inferiore:

// Done within a Grand Central Dispatch block, or NSOperation. 

// We do not start the connection here because we still need to attach the connection to the RunLoop of the current thread and handle how it will communicate responses back to the caller. 
theConnection = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO]; 

// A previously instantiated NSOperationQueue for managing the delegate callbacks if-and-when they occur. 
// [theConnection setDelegateQueue:delegateQueue]; 
/* 
// Other NSURLConnection logic, etc. 
*/  
// We start the connection manually after properly establishing how it will poll and respond to events. 
[theConnection start]; 

// This is for the RunLoop of the current thread. (Only needed for < iOS 6 compatibility) 
// If this method is executed inside a GCD block or NSOperation, it will be the RunLoop of the thread run in-parallel to the Main Thread. 
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:30.0]]; 
// From here, you can alter the time interval to coincide with a specific "time-out" event you'd like to occur. 

Dove "theConnection" è una variabile membro della classe corrente di tipo "NSURLConnection". Inoltre, sarà necessario creare una variabile membro NSOperationQueue per gestire i callback delegati una volta che la connessione riceve una risposta. Queste chiamate verranno comunicate in modo asincrono al thread che sta eseguendo la connessione.

Da lì, è possibile restituire i dati utilizzando i metodi appropriati NSURLConnection delegate.

Il vantaggio dell'utilizzo di Grand Central Dispatch, o Operation Queues per i thread, è che i meccanicismi Threading e RunLoop sono già integrati; NON dovrai allocare manualmente un thread aggiuntivo con il proprio RunLoop al suo interno. Ciò elimina la ridondanza in due fasi della creazione di un thread in background per gestire le chiamate asincrone del server.

Spero che questo sia sufficiente per ottenere la giusta strada per la creazione di un modello di rete veramente asincrono per la vostra applicazione.:)

+1

Se si utilizza 'setDelegateQueue' (iOS 5+), non è necessario' scheduleInRunLoop'. Come [docs] (http://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/Reference/Reference.html#//apple_ref/doc/uid/20001697-SW3) "È un errore pianificare i messaggi delegati sia con questo metodo [' setDelegateQueue'] che con il metodo 'scheduleInRunLoop: forMode:' ". Si usi 'scheduleInRunLoop' se si crea un' NSThread' e il proprio runloop. Usa 'setDelegateQueue' se vuoi usare' NSOperationQueue' (che è più facile, IMHO). – Rob

+0

Eccellente osservazione! Adatterò il mio codice di esempio per riflettere ciò che hai detto. Grazie. – user298261

+0

@Rob Ho appena apportato alcune modifiche al codice e ottengo una risposta più rapida sul mio telefono utilizzando: [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 30.0]]. Molto interessante ... :) Correzione – user298261

1

È possibile avviare un NSOperationQueue temporaneo per quella connessione. Questa coda vivrà solo finché la connessione ne ha bisogno. Fondamentalmente, NSOperationQueue garantisce che i callback delegati vengano accodati ed elaborati filando un thread per elaborare ogni callback delegato. (Nella maggior parte dei casi è lo stesso thread in background che viene messo in pausa e ripreso quando vengono scaricati nuovi dati, o quando la connessione non riesce, la connessione termina il caricamento ecc.). Una volta che hai configurato questa coda, i callbacks entreranno nella tua applicazione.

Se si sceglie un RunLoop, la gestione del runloop è un onere aggiuntivo per la propria parte.