NSURLConnection против NSData + GCD
NSData
всегда имел очень удобный метод, называемый +dataWithContentsOfURL:options:error:
. Хотя это удобно, оно также блокирует выполнение текущего потока, что означает, что он был практически бесполезен для производственного кода (Игнорирование NSOperation
). Я использовал этот метод так редко, я полностью забыл, что он существует. До недавнего времени.
То, как я собирал данные из трубок, является стандартным подходом NSURLConnectionDelegate
: напишите класс загрузки, который обрабатывает различные методы NSURLConnectionDelegate
, постепенно наращивает некоторые данные, обрабатывает ошибки и т.д. Я обычно сделайте это общее достаточно для повторного использования для максимально возможного количества запросов.
Скажем, мой типичный класс загрузчика запускается где-то на стадионе 100 строк. Это 100 строк, чтобы сделать асинхронно то, что NSData
может выполнять синхронно в одной строке. Для большей сложности этому классу загрузчика нужен собственный протокол делегирования, чтобы сообщить о завершении и ошибках его владельцу, и владельцу необходимо каким-то образом реализовать этот протокол.
Теперь войдите в Grand Central Dispatch, и я могу сделать что-то столь же фантастически просто, как:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
NSData* data = [NSData dataWithContentsOfURL:someURL];
// Process data, also async...
dispatch_async(dispatch_get_main_queue(), ^(void) {
// Back to the main thread for UI updates, etc.
});
});
И я могу бросить эту присоску в любом месте, где хочу, прямо в строке. Нет необходимости в классе загрузки, нет необходимости обрабатывать методы делегирования соединений. Легкие данные async всего в нескольких строках. Несоответствие между этим подходом и моим пред-GCD-подходом имеет такую величину, которая достаточно велика, чтобы вызвать Too Good, чтобы быть True Alarm.
Таким образом, мой вопрос: Существуют ли какие-либо предостережения для использования NSData
+ GCD для простых задач загрузки данных вместо NSURLConnection
(Предполагая, что меня не интересуют такие вещи, как процесс загрузки)?
Ответы
Ответ 1
Здесь вы теряете много функциональности:
- Невозможно выполнить последовательность загрузки
- Невозможно отменить загрузку
- Невозможно управлять возможным процессом аутентификации
- Вы не можете легко обрабатывать ошибки, которые действительно важны, особенно в мобильных разработках, например, на iPhone (потому что вы часто теряете свою сеть в реальных условиях, поэтому очень важно отслеживать такие сетевых ошибок при разработке для iOS)
и, вероятно, больше я думаю.
Правильный подход для этого - создать класс, чем управлять загрузкой.
Посмотрите мой собственный OHURLLoader
класс, который прост, и я сделал API простым в использовании с блоками:
NSURL* url = ...
NSURLRequest* req = [NSURLRequest requestWithURL:url];
OHURLLoader* loader = [OHURLLoader URLLoaderWithRequest:req];
[loader startRequestWithCompletion:^(NSData* receivedData, NSInteger httpStatusCode) {
NSLog(@"Download of %@ done (statusCode:%d)",url,httpStatusCode);
if (httpStatusCode == 200) {
NSLog(%@"Received string: %@", loader.receivedString); // receivedString is a commodity getter that interpret receivedData using the TextEncoding specified in the HTTP response
} else {
NSLog(@"HTTP Status code: %d",httpStatusCode); // Log unexpected status code
}
} errorHandler:^(NSError *error) {
NSLog(@"Error while downloading %@: %@",url,error);
}];
Для получения дополнительной информации см. файл README и пример проекта на github.
Таким образом:
- вы по-прежнему полагаетесь на асинхронные методы, предоставляемые NSURLConnection (и поскольку в документации Apple говорится о Concurrency Программирование, если API уже существует для выполнения асинхронных задач, используйте его, а не полагайтесь на другую технологию потоков, если это возможно)
- Вы сохраняете преимущества NSURLConnection (обработки ошибок и т.д.).
- но у вас также есть преимущества синтаксиса блоков, которые делают ваш код более понятным, чем при использовании методов делегата.
Ответ 2
Видеозапись сеанса WWDC 2010:
- WWDC 2010 Session 207 - Сетевые приложения для iPhone OS, часть 1
- WWDC 2010 Session 208 - Сетевые приложения для iPhone OS, часть 2
Лектор сказал
"Threads Evil ™".
Для сетевого программирования настоятельно рекомендуется использовать асинхронный API с RunLoop.
Поскольку, если вы используете NSData + GCD, как показано ниже, он использует один поток для каждого соединения.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
NSData* data = [NSData dataWithContentsOfURL:someURL];
И он, вероятно, будет использовать множество соединений и множество потоков. Слишком просто использовать GCD:-)
Затем многие потоки питаются огромным объемом памяти для своего стека.
Таким образом, вам лучше использовать асинхронный API, как сказал AliSoftware.
Ответ 3
Как и в OS X v10.9 и iOS 7, предпочтительным способом является использование NSURLSession. Это дает вам приятный, основанный на блоках интерфейс и функции, такие как отмена, приостановка и загрузка фона.