Ответ 1
Я собираюсь решить их в обратном порядке. Вы спрашиваете:
Итак, это подводит меня к моему второму вопросу, почему [queue setMaxConcurrentOperationCount: 1] имеет такое большое влияние в моем коде ниже? Из документации я думал, что оставить maxConcurrentOperationCount по умолчанию по умолчанию, и это просто говорит очереди определить, какое наилучшее значение должно быть основано на определенных факторах.
С NSURLConnection
вы не можете одновременно загружать более четырех или пяти подключений. Таким образом, если вы не устанавливаете maxConcurrentOperationCount
, очередь операций не знает, что вы имеете дело с NSURLConnection
, и поэтому, когда вы добавляете 300 NSOperation
объектов в очередь, очередь будет пытаться запустить очень большой число из них (64-х, я думаю) одновременно. Но так как запросы только 4 или 5 NSURLConnection
могут выполняться одновременно, остальные из них, которые были запущены в очередь, будут ждать, пока доступно одно из четырех или пяти возможных подключений, и с таким количеством запросов на загрузку вполне вероятно, что многие из них будут тайм-аут и неудача.
Используя maxConcurrentOperationCount
of 1, это применит довольно жесткое решение этой проблемы, только запустив один за раз. Я бы предложил компромисс, а именно maxConcurrentOperationCount
of 4, который имеет степень concurrency (и огромный прирост производительности), но не так много, что мы рискуем иметь время соединения и выйти из строя.
Возвращаясь к Дейву Друбину NSOperation
, он значительно улучшился по сравнению с вашим synchronousRequest
, завернутым в операцию. Сказав это, он пренебрег рассмотрением довольно простой функции одновременных запросов, а именно отмены. Вы должны включить проверку, чтобы проверить, была ли операция отменена, и если это так, отмените соединение:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
if ([self isCancelled]) {
[connection cancel];
[self finish];
return;
}
[_data appendData:data];
}
Аналогично, когда он должен делать это в методе start
тоже.
- (void)start
{
// The Apple docs say "Always check for cancellation before launching the task."
if ([self isCancelled]) {
[self willChangeValueForKey:@"isFinished"];
_isFinished = YES;
[self didChangeValueForKey:@"isFinished"];
return;
}
if (![NSThread isMainThread])
{
[self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
return;
}
NSLog(@"opeartion for <%@> started.", _url);
[self willChangeValueForKey:@"isExecuting"];
_isExecuting = YES;
[self didChangeValueForKey:@"isExecuting"];
NSURLRequest * request = [NSURLRequest requestWithURL:_url];
_connection = [[NSURLConnection alloc] initWithRequest:request
delegate:self];
if (_connection == nil)
[self finish];
}
Я мог бы предложить другие стилистические улучшения к примеру Дэйва, но все это мелкие вещи, и я думаю, что он получил большую часть большого места на картинке. Невозможность проверить отмену была единственной очевидной большой проблемой, которая выскочила на меня.
В любом случае, для обсуждения параллельных операций см. раздел "Конфигурирование операций для параллельного выполнения" Concurrency Руководство по программированию.
Кроме того, при тестировании огромных загрузок, подобных этим, я бы посоветовал вам протестировать свое приложение с помощью Network Link Conditioner (доступного для Mac/simulator в качестве загрузки, доступной в разделе "Оборудование IO-инструментов" на "Xcode" - "Open Developer Tool" - "Дополнительные инструменты для разработчиков", если вы включите устройство iOS для разработки, в настройках также есть настройка настройки сетевой ссылки в разделе "Общие" - "Разработчик" ). Многие из этих проблем, связанных с таймаутом, не проявляются, когда мы тестируем наши приложения в нашем высоко оптимизированном сценарии нашей среды разработки. Важно использовать средство связи с сетью для моделирования менее реалистичных сценариев реального мира.