2015-07-10 24 views
5

Sto provando a creare un foglio xls a livello di programmazione. Per riempire il foglio, sto facendo il multiplo NSURLConnection circa 100. In questo momento, il mio approccio è:Il modo migliore per gestire più connessioni NSURL

  1. Fai un collegamento e memorizzare i dati in un array. Questa matrice ha 100 oggetti.
  2. Ora prendi il primo oggetto e chiama la connessione. Memorizza i dati. E crea la seconda connessione con il 2o oggetto nell'array. Questo continua fino all'ultimo oggetto nella matrice.

In media ci vogliono 14 secondi per completare i 100 collegamenti. C'è un modo per implementare lo NSURLConnection per ottenere la risposta in modo più rapido?

Fino a ieri ho seguito l'approccio di base come:

Dichiarazione di proprietà:

@property (nonatomic,strong) NSURLConnection *getReportConnection; 
@property (retain, nonatomic) NSMutableData *receivedData; 
@property (nonatomic,strong) NSMutableArray *reportArray; 

Inizializzazione dell'array in viewDidLoad:

reportArray=[[NSMutableArray alloc]init]; 

Inizializzazione della NSURLConnection in azione pulsante:

/initialize url that is going to be fetched. 
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"****/%@/crash_reasons",ID]]; 

//initialize a request from url 
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; 
[request addValue:tokenReceived forHTTPHeaderField:@"**Token"]; 

[request setHTTPMethod:@"GET"]; 
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; 

//initialize a connection from request 
self.getReportConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; 

L'elaborazione dei dati ricevuti:

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData*)data{ 
if (connection==_getVersionConnection) { 

    [self.receivedData_ver appendData:data]; 

    NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 

    NSError *e = nil; 
    NSData *jsonData = [responseString dataUsingEncoding:NSUTF8StringEncoding]; 

    NSDictionary *JSON = [NSJSONSerialization JSONObjectWithData:jsonData options: NSJSONReadingMutableContainers error: &e]; 
    [JSON[@"app_versions"] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 
     if (![obj[@"id"] isEqual:[NSNull null]] && ![reportArray_ver containsObject:obj[@"id"]]) { 

      [reportArray_ver addObject:obj[@"id"]]; 

     } 
     NSLog(@"index = %lu, Object For title Key = %@", (unsigned long)idx, obj[@"id"]); 
    }]; 

    if (JSON!=nil) { 
     UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"Version Reports succesfully retrieved" message:@"" delegate:self cancelButtonTitle:@"Ok" otherButtonTitles: nil]; 
     [alert show]; 
    } 
} 

} 

Chiamando il un'altra connessione dopo uno finiture:

// This method is used to process the data after connection has made successfully. 
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{ 
    if (connection==getReportConnection) { 

      //check and call the connection again 
    } 
} 

E oggi, ho provato il NSURLConnection con sendAsync a fuoco tutte le connessioni uno dopo altro ciclo usando e ha funzionato abbastanza bene.

self.receivedData_ver=[[NSMutableData alloc]init]; 
__block NSInteger outstandingRequests = [reqArray count]; 
for (NSString *URL in reqArray) { 

    NSMutableURLRequest *request=[NSMutableURLRequest requestWithURL:[NSURL URLWithString:URL] 
                 cachePolicy:NSURLRequestUseProtocolCachePolicy 
                timeoutInterval:10.0]; 

    [request setHTTPMethod:@"GET"]; 
    [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; 


[NSURLConnection sendAsynchronousRequest:request 
            queue:[NSOperationQueue mainQueue] 
         completionHandler:^(NSURLResponse *response, 
              NSData *data, 
              NSError *connectionError) { 

          [self.receivedData appendData:data]; //What is the use of appending NSdata into Nsmutable data? 

          NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 

          NSError *e = nil; 
          NSData *jsonData = [responseString dataUsingEncoding:NSUTF8StringEncoding]; 

          NSDictionary *JSON = [NSJSONSerialization JSONObjectWithData:jsonData options: NSJSONReadingMutableContainers error: &e]; 
          NSLog(@"login json is %@",JSON); 

          [JSON[@"app_versions"] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 


           if (![obj[@"id"] isEqual:[NSNull null]] && ![reportArray_ver containsObject:obj[@"id"]]) { 

            [reportArray_ver addObject:obj[@"id"]]; 

           } 

           NSLog(@"index = %lu, Object For title Key = %@", (unsigned long)idx, obj[@"id"]); 
          }]; 


          outstandingRequests--; 

          if (outstandingRequests == 0) { 
           //all req are finished 
           UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"Version Reports succesfully retrieved" message:@"" delegate:self cancelButtonTitle:@"Ok" otherButtonTitles: nil]; 
           [alert show]; 
          } 

         }]; 
} 

Questa volta ha preso la metà del tempo per completare le 100 richieste rispetto alla vecchia procedura, C'è un modo più veloce esiste altro che l'asynReq? .Che è lo scenario migliore per utilizzare NSURLconnection e NSURLConnection with asyncReq?

+0

Perché non si utilizza NSURLSession? NSURLConnection è ora deprecato –

risposta

6

Un paio di osservazioni:

  1. Usa NSURLSession piuttosto che NSURLConnection (se si stanno sostenendo iOS versioni 7.0 e superiori):

    for (NSString *URL in URLArray) { 
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL]; 
    
        // configure the request here 
    
        // now issue the request 
    
        NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { 
         // check error and/or handle response here 
        }]; 
        [task resume]; 
    } 
    
  2. Se dovete assolutamente di emettere 100 richieste , quindi emetterli contemporaneamente come l'implementazione sendAsynchronousRequest (o il mio dataTaskWithRequest), non in sequenza. Questo è ciò che raggiunge l'enorme vantaggio prestazionale.

    nota, però, che si dispone di alcuna garanzia che faranno del tutto nell'ordine in cui li emessi, così si vuole utilizzare una qualche struttura che supporta tale (ad esempio l'uso NSMutableDictionary o pre-popolano la NSMutableArray con i segnaposto giù di lì puoi semplicemente aggiornare la voce su un particolare indice piuttosto che aggiungere un elemento all'array).

    In conclusione, tenere presente che potrebbero non essere completati nello stesso ordine richiesto, quindi assicurarsi di gestirlo in modo appropriato.

  3. Se si mantiene a 100 richieste separate, io suggerirei di provare questo su una connessione di rete molto lenta (ad esempio utilizzare il collegamento di rete condizionatore per simulare connessione di rete davvero male, vedi NSHipster discussion). Ci sono problemi (timeout, problemi di interfaccia utente, ecc.) Che compaiono solo quando si esegue questa operazione su una connessione lenta.

  4. Invece di decrementare un contatore del numero di richieste in sospeso, suggerirei di utilizzare gruppi di invio o dipendenze della coda di operazioni.

    dispatch_group_t group = dispatch_group_create(); 
    
    for (NSString *URL in URLArray) { 
        dispatch_group_enter(group); 
    
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL]; 
    
        // configure the request here 
    
        // now issue the request 
    
        NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { 
         // check error and/or handle response here 
    
         // when all done, leave group 
    
         dispatch_group_leave(group); 
        }]; 
        [task resume]; 
    } 
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{ 
        // do whatever you want when all of the requests are done 
    }); 
    
  5. Se possibile, vedere se è possibile refactoring il servizio web in modo che si sta emissione sola richiesta che restituisce tutti i dati. Se stai cercando un ulteriore miglioramento delle prestazioni, questo è probabilmente il modo di farlo (e evita molte complessità coinvolte nell'emissione di 100 richieste separate).

  6. A proposito, se si usa la connessione basata delegato, come hai fatto nella tua domanda iniziale, si dovrebbe non essere l'analisi dei dati in didReceiveData. Questo dovrebbe solo aggiungere dati a NSMutableData. Esegui tutto il parsing nel metodo delegato connectionDidFinishLoading.

    Se si passa all'implementazione basata su blocchi, questo problema scompare, ma solo un'osservazione sugli snippet di codice.

+0

@ Rob- Ho provato a inserire la sessione url nel ciclo ma, non è stata eseguita. In qualche modo, il blocco di codice all'interno della sessione dell'URL viene saltato. – ZeeroCool77

+0

@ T_77 - Bene, quindi esegui alcune operazioni di diagnostica di base con istruzioni o punti di interruzione del registro, assicurandoti di colpire la riga 'riprendi' come dovresti. Ma questo schema per l'uso di 'NSURLSession' è piuttosto standard, quindi sospetto che il problema non sia correlato. Le richieste stanno iniziando correttamente? Prova a guardare le richieste usando [Charles] (http://charlesproxy.com). – Rob

+0

@ Rob-it ha funzionato. Potrei anche sorvolare la tua risposta, ma mi dispiace che non ho abbastanza rep !!! Grazie per la risposta gr8. – ZeeroCool77

1

Utilizzando sendAsynchronous è un ottimo modo per migliorare l'organizzazione del codice. Sono sicuro che con un attento esame potremmo migliorare la velocità al margine, ma il modo per migliorare sensibilmente la velocità è di non effettuare 100 richieste.

Se i corpi di risposta sono piccoli, creare un endpoint che risponda a una combinazione dei risultati.

Se i corpi di risposta sono di grandi dimensioni, allora si sta richiedendo più dati di quelli che l'utente ha bisogno in questo momento. Afferra l'interfaccia utente solo su ciò che l'utente deve vedere, e prendi il resto in silenzio (... o, forse meglio che in silenzio, pigramente).

Se non si controlla il server e i corpi di risposta sono piccoli e l'utente ha bisogno di tutti o quasi per continuare con l'app, è possibile iniziare a lavorare sulle prestazioni ai margini e trucchi dell'interfaccia utente per divertire utente mentre l'app funziona, ma solitamente uno di questi vincoli, di solito il secondo, può essere rilassato.

+0

Durante le connessioni, visualizzo semplicemente la barra di avanzamento e la rimuovo una volta terminata la connessione. – ZeeroCool77