2013-03-23 13 views
10

Ho riscontrato problemi con il download di 5 MB di file, ci sono voluti più di 2 minuti su iPhone 5 con iOS 6.1.NSURLConnection download molto lungo

Utilizzando iPhone 4S con la stessa versione di iOS richiede solo 10 secondi, entrambi utilizzano il WiFi.

Ho provato diversi criteri di cache e intervallo di timeout di NSURLRequest, non ha cambiato nulla, richiede ancora molto tempo. Il download è su HTTP.

Sto usando NSURLConnection classe, prima di scaricare questo file "grande" sto scaricando altri due.

Non so che altro può essere importante, per ridurre il tempo.

Grazie in anticipo.

Codice:

@interface MyClass : NSObject <NSURLConnectionDelegate> 
{ 
    @private id delegate; 
    NSURLConnection *connection; 
    NSMutableData* responseData; 
    //... 
} 


#import "MyClass.h" 

@implementation MyClass 

-(void)getObj1:(id)delegateObj 
{ 
    delegate = delegateObj; 

    NSString *url = @"..."; 

    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:120.0]; 

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

    if(connection) 
    { 
     responseData = [NSMutableData data]; 
    } 
} 

-(void)getObj2:(*String)somesString 
{ 

    NSString *url = @"..."; 

    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:120.0]; 

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

    if(connection) 
    { 
    responseData = [NSMutableData data]; 
    } 
} 

-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response 
{ 
    //.... 
} 


-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 
{ 
    [responseData appendData:data]; 
} 

- (void)connectionDidFinishLoading:(NSURLConnection *)connection 
{ 
    if(firstRequest) 
    { 
     //save data from first request and set responseData clear 
     [self getObj2:@"..."]; 
    } 
} 

ed altri senza nulla di speciale, spero che questo sarà sufficiente

Ho trovato questo post https://devforums.apple.com/message/754875#754875, ma ancora non funziona bene per me. Comunque ora capisco meglio questa strana situazione.

+0

Il tuo codice è importante. Se è possibile pubblicare la modalità di creazione e avvio della richiesta e i metodi di delega. –

+0

Qualche idea? Forse questo link dalla mia modifica mi aiuterà, sono a corto di idee – Meryl

+0

Dovresti piuttosto creare _two_ oggetti del tuo 'MyClass' per eseguire _due_ download diversi. Puoi rapidamente confondere gli ivar. Inoltre, assicurarsi di invocare [[NSURLConnection alloc] init ...] su quel thread in cui si desidera eseguire il delegato. Forse il thread principale - altrimenti devi sapere come impostare un thread secondario per quello scopo che è un po 'complicato. – CouchDeveloper

risposta

0

Hai provato AFNetworking? È un wrapper su NSURLConnection. Non sono sicuro che sarebbe utile per ottenere un download più veloce, ma sicuramente ti dà un vantaggio rispetto a NSURLConnection.

+0

Basato su questo argomento http://stackoverflow.com/questions/12570483/ios6-large-downloads-time-out?rq=1 Suppongo che non sarà di aiuto, ma lo controllerò nel prossimo passaggio. Grazie per la risposta. – Meryl

+0

Ho provato ma ci vuole ancora la stessa quantità di tempo – Meryl

1

Utilizzare AFDownloadRequestOperation (AFNetworking "sublass") - è anche possibile sospendere/riprendere il funzionamento.

Ecco un esempio https://stackoverflow.com/a/12209618

+0

Non ho un progetto ARC e ho qualche problema con questa classe – Meryl

0

hai usato GCD dispatch_async coda per eseguire il set di richiesta NSURLRequest per scaricare dati dal server o di ottenere risposta del server.

NSString *webURL = @"http://therealurl.appspot.com/?format=json&url=bit.ly/a"; 
NSURL *url = [NSURL URLWithString:webURL]; 
NSURLRequest *awesomeRequest = [NSURLRequest requestWithURL:url]; 
NSURLConnection *connection=[[NSURLConnection alloc] initWithRequest:awesomeRequest delegate:self]; 
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul); 
dispatch_async(queue, ^{ 
    NSRunLoop *loop=[NSRunLoop currentRunLoop]; 
    [connection scheduleInRunLoop:loop forMode:NSRunLoopCommonModes]; 
    //[self processTheAwesomeness]; 
}); 
+1

Una connessione non dovrebbe essere pianificata tramite 'scheduleInRunLoop: forMode:' in una coda di invio che cerca di ottenere il suo ciclo di esecuzione . Ciò non funzionerà poiché il ciclo di esecuzione può scomparire in qualsiasi momento (se necessario) e il thread di sottofondo può cambiare da delegato a delegato in qualsiasi momento. – CouchDeveloper

+0

Quello che ha detto. Se vuoi gestire la tua programmazione come questa e tenerla lontana dal thread principale, devi generare (e gestire) il thread in background. Detto questo, nel caso di un singolo trasferimento, senza altre attività significative sul thread principale, non ci sarebbe motivo di non pianificarlo semplicemente sul thread principale. – ipmcc

0

Non sono sicuro di quale sia il problema, ma il codice seguente funziona in modo affidabile per me.

- (id)init 
{ 
    self.downloadQueue = [[NSOperationQueue alloc] init]; 
    [self.downloadQueue setMaxConcurrentOperationCount:1]; 
} 

- (void)doDownload:(NSURL *)url 
{ 
    [self.downloadQueue addOperation:[NSBlockOperation blockOperationWithBlock:^{ 

    NSData *data =[NSData dataWithContentsOfURL:url]; 

    dispatch_sync(dispatch_get_main_queue(), ^{ 
     NSAutoreleasePool *mainQueuePool = [[NSAutoreleasePool alloc] init]; 

     ... update user interface ... 

     [mainQueuePool release]; 
    }); 

    }]]; 
} 
-1

Penso che sia un problema nel dispositivo. Prova un altro dispositivo da un amico.

0

Proporrei una soluzione in cui si dispone di una sorta di gestore di comunicazione che ha un NSOperationQueue che gestisce, con un metodo di punto di ingresso singolo in cui si dà un URL in cui il contenuto vive che si desidera scaricare e un successo e un blocco di fallimento. Il responsabile della comunicazione crea uno NSOperation dove si crea lo NSURLRequest e si gestiscono i callback.

Non appena il responsabile di comunicazione immette l'operazione in coda, viene chiamato il metodo start. Nella mia implementazione del gestore delle comunicazioni tengo traccia (oltre a mettere le operazioni in coda) di ogni operazione avviata tramite un NSMutableDictionary in modo da poter annullare una singola operazione o tutte (Nel codice di esempio fornito allo operationKey viene utilizzato per questo scopo. il JSONOperation restituisce (in caso di successo) una NSString al comunicatore ma potrebbe anche essere qualsiasi tipo di dati, ad esempio utilizzo la stessa classe per il download delle immagini, quindi passerò nuovamente l'oggetto dati stesso. Qui di seguito è possibile trovare la mia classe JSONOperation come esempio.Mi piace l'idea di mettere gli altri file su Gist.

mio NSOperation assomiglia a questo

@interface JSONOperation : NSOperation <NSURLConnectionDataDelegate, OperationDelegate> 
    + (instancetype)jsonOperationForProvider:(id)provider success:(OperationSuccessWithString)success failure:(OperationFailure)failure; 
@end 

#import "JSONOperation.h" 
#import "ProviderDelegate.h" 

@interface JSONOperation() 
@property (nonatomic, assign) BOOL executing; 
@property (nonatomic, assign) BOOL finished; 
@property (nonatomic, assign) BOOL cancelled; 

@property (nonatomic, strong) NSURL *url; 

@property (nonatomic, weak) id <ProviderDelegate> delegate; 
@property (nonatomic, strong) NSURLConnection *connection; 
@property (nonatomic, strong) NSMutableData *receivedData; 

@property (nonatomic, copy) OperationFailure failure; 
@property (nonatomic, copy) OperationSuccessWithString success; 
@end 

@implementation JSONOperation 

- (void)start 
{ 
    if ([self isCancelled]) 
{ 
    [self willChangeValueForKey:@"isFinished"]; 
    _finished = YES; 
    [self didChangeValueForKey:@"isFinished"]; 

    return; 
} 

    NSURLRequest *request = [NSURLRequest requestWithURL:self.url]; 
    self.connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; 
    [self.connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; 
    [self.connection start]; 

    [self willChangeValueForKey:@"isExecuting"]; 
    _executing = YES; 
    [self didChangeValueForKey:@"isExecuting"]; 
} 

- (NSMutableData *)receivedData 
{ 
    if (nil == _receivedData) { 
     _receivedData = [NSMutableData data]; 
    } 
    return _receivedData; 
} 

+ (instancetype)jsonOperationForProvider:(id <ProviderDelegate>)provider success:(OperationSuccessWithString)success failure:(OperationFailure)failure 
{ 
    NSAssert(nil != provider, @"provider parameter can't be nil"); 

    JSONOperation *operation = [[[self class] alloc] init]; 
    operation.delegate = provider; 
    operation.url = provider.contentURL; 
    operation.failure = failure; 
    operation.success = success; 

    return operation; 
} 

- (BOOL)isConcurrent { 
    return YES; 
} 

- (BOOL)isExecuting { 
    return _executing; 
} 

- (BOOL)isFinished { 
    return _finished; 
} 

- (BOOL)isCancelled { 
    return _cancelled; 
} 

#pragma mark - NSURLConnectionDataDelegate 

- (void)connectionDidFinishLoading:(NSURLConnection *)connection { 

    if (_success) { 
     NSString *receivedText = [[NSString alloc] initWithData:self.receivedData encoding:NSUTF8StringEncoding]; 
     _receivedData = nil; 

     self.success(self, receivedText); 
    } 

    [self willChangeValueForKey:@"isFinished"]; 
    [self willChangeValueForKey:@"isExecuting"]; 

    _executing = NO; 
    _finished = YES; 

    [self didChangeValueForKey:@"isExecuting"]; 
    [self didChangeValueForKey:@"isFinished"]; 
} 

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { 
    [self.receivedData appendData:data]; 
} 

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error 
{ 
    [connection cancel]; 
    _connection = nil; 
    _receivedData = nil; 
    _url = nil; 

    if (_failure) { 
     [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 
      self.failure(self, error); 
    }]; 
    } 

    [self willChangeValueForKey:@"isFinished"]; 
    [self willChangeValueForKey:@"isExecuting"]; 

    _executing = NO; 
    _finished = YES; 

    [self didChangeValueForKey:@"isExecuting"]; 
    [self didChangeValueForKey:@"isFinished"]; 
} 

#pragma mark - OperationDelegate 

- (NSString *)operationKey 
{ 
    return [self.url absoluteString]; 
} 

- (id)provider 
{ 
    return _delegate; 
} 

- (void)cancelOperation 
{ 
    _failure = nil; 
    _success = nil; 

    [self.connection cancel]; 
    _connection = nil; 

    _receivedData = nil; 
    _url = nil; 

    [self willChangeValueForKey:@"isCancelled"]; 
    [self willChangeValueForKey:@"isFinished"]; 
    [self willChangeValueForKey:@"isExecuting"]; 

    _executing = NO; 
    _finished = YES; 
    _cancelled = YES; 

    [self didChangeValueForKey:@"isCancelled"]; 
    [self didChangeValueForKey:@"isExecuting"]; 
    [self didChangeValueForKey:@"isFinished"]; 
} 

@end 

EDIT - Gist Sample files

0

bene nella mia esperienza AFNetworking fa un lavoro awsome nella gestione della downloads.I sto usando un'operazione di scaricamento su file con 10+ Dimensione MB. Quindi ti consiglio caldamente di usarlo.

My answer on stack to show progressbar .Vedere la risposta in cui ho implementare entrambe le vie, con AFNetworking e NSUrlconnection .È possibile provare entrambi i modi e possono vedere i progressi e si può calcolare quanto i byte vengono scaricati in ogni packet.By di monitoraggio in modo da poter analizzare come varia nel tempo di download. Provalo

+0

@Meryl: fai un tentativo e fammi sapere come è andata –

0

Basta provare a utilizzare gzip per comprimere il file remoto per NSURLRequest. Accelererà la tua connessione in modo drammatico.

Per utilizzare questo, è necessario averlo installato sul server, e la buona notizia è che se si utilizza apache2 sul server, viene fornito per impostazione predefinita. Per eseguire il test per assicurarsi che il vostro server/URL aveva gzip compressione abilitata, prova con questo strumento online: http://www.feedthebot.com/tools/gzip/

Se la risposta è sì, procedere per aggiungere il codice per il codice Objective-C in Xcode. Dopo questa riga nel codice:

NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:120.0]; 

Basta aggiungere questo:

// Create a mutable copy of the immutable request and add more headers 
NSMutableURLRequest *mutableRequest = [request mutableCopy]; 

//add gzip compression 
[mutableRequest addValue:@"gzip" forHTTPHeaderField:@"Accept-Encoding"]; 

// Now set our request variable with an (immutable) copy of the altered request 
request = [mutableRequest copy]; 

Questo sarà accelerare il tempo di risposta notevolmente, e non c'è bisogno di utilizzare AFNetworking per un piccolo NSURLRequest o NSURLConnection compito.