NSURLConnection очень длительная загрузка

У меня возникла проблема с загрузкой 5 Мбайт файла, это занимает более 2 минут на iPhone 5 с iOS 6.1.

Используя iPhone 4S с той же версией iOS, он занимает всего 10 секунд, оба используют WiFi.

Я пробовал различную политику и интервал тайм-аута NSURLRequest, он ничего не менял, он все еще занимает много времени. Загрузка осуществляется через HTTP.

Я использую класс NSURLConnection, перед загрузкой этого "большого" файла я загружаю два других.

Не знаю, что еще может быть важно, чтобы сократить время.

Спасибо заранее.

код:

@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:@"..."];
   }
}

и другие, не имеющие ничего особенного, надеюсь, этого будет достаточно

Я нашел этот пост https://devforums.apple.com/message/754875#754875, но все еще не работает нормально для меня. Однако теперь я лучше понимаю эту странную ситуацию.

Ответ 1

Использовать AFDownloadRequestOperation (AFNetworking "sublass" ) - вы можете также приостановить/возобновить работу.

Здесь у вас есть пример fooobar.com/questions/539490/...

Ответ 2

Вы пробовали AFNetworking? Это оболочка на NSURLConnection. Я не уверен, поможет ли вам ускорить загрузку, но это наверняка даст вам преимущество над NSURLConnection.

Ответ 3

Вы использовали очередь GCD dispatch_async для выполнения набора запросов NSURLRequest для загрузки данных с сервера или получения ответа сервера.

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];
});

Ответ 4

Я не уверен, в чем проблема, но следующий код работает для меня надежно.

- (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];
    });

  }]];
}

Ответ 5

Я бы предложил решение, в котором у вас есть менеджер связей, который имеет NSOperationQueue, который обрабатывает, с одним методом точки входа, где вы указываете ему URL-адрес, в котором находится контент, который вы хотите загрузить, и успех и отказ. Менеджер связи создает NSOperation, где вы создаете NSURLRequest и обрабатываете обратные вызовы.

Как только менеджер связи отправит операцию в очередь, вызывается метод start. В моей реализации менеджера связи я отслеживаю (помимо ввода операций в очередь) каждой запущенной операции через NSMutableDictionary, чтобы вы могли отменить одну или все операции (в примере кода, который используется для operationKey для этого Здесь JSONOperation возвращает (в случае успеха) NSString для коммуникатора, но это могут быть и любые типы данных, например, я использую один и тот же класс для загрузки изображений, поэтому я передал бы сам объект данных. Ниже вы можете найти мой класс JSONOperation. Мне нравится идея, что я могу поместить другие файлы в Gist.

Мой NSOperation выглядит следующим образом

@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

Ответ 6

В моем опыте AFNetworking выполняет огромную работу по обработке загрузок. Я использую операцию загрузки файлов с размером 10+ МБ. Поэтому я настоятельно рекомендую использовать его.

Мой ответ в стеке, чтобы показать прогресс-панель. Отвечайте, где я реализую оба способа: AFNetworking и NSUrlconnection. Вы можете попробовать оба способа и можно увидеть прогресс, и вы можете рассчитать, как байты загружаются в каждом пакете. Отслеживайте его, чтобы вы могли анализировать, как он меняется во время загрузки. Попробуйте

Ответ 7

Просто попробуйте использовать gzip для сжатия удаленного файла для NSURLRequest. Это значительно ускорит ваше соединение.

Чтобы использовать это, вам необходимо установить его на сервере, и хорошая новость заключается в том, что вы используете apache2 на своем сервере, и он приходит по умолчанию. Чтобы проверить, чтобы ваш сервер/URL-адрес имел gzip сжатие, проверьте его с помощью этого онлайн-инструмента: http://www.feedthebot.com/tools/gzip/

Если да, перейдите к добавлению кода в код Objective-C в Xcode. После этой строки в вашем коде:

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

просто добавьте это:

// 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];

Это значительно ускорит ваше время отклика, и вам не нужно использовать AFNetworking для небольшой задачи NSURLRequest или NSURLConnection.

Ответ 8

Я думаю, что это проблема на вашем устройстве. Попробуйте другое устройство у друга.