С точки зрения разработчика, одним из наиболее значительных изменений в iOS 7 и OS X Mavericks в этом отношении является введение NSURLSession
. Несмотря на то, что NSURLSession
может показаться пугающим на первый взгляд, важно, чтобы вы поняли, что это такое, как оно связано с NSURLConnection
и каковы различия. В этой серии я NSURLSession
вас с основами NSURLSession
чтобы вы могли использовать эту новую технологию в своих собственных приложениях.
Зачем NSURLConnection
?
Первый вопрос, который вы можете себе задать, — это то, почему Apple посчитала необходимым ввести NSURLSession
то время как мы полностью довольны NSURLConnection
. Реальный вопрос в том , довольны ли вы NSURLConnection
. Вы помните тот момент, когда вы NSURLConnection
и бросали вещи в NSURLConnection
? Большинство разработчиков Cocoa были именно в этом месте, поэтому Apple решила вернуться к чертежной доске и создать более элегантное решение, более подходящее для современного Интернета.
Хотя NSURLSession
и NSURLConnection
имеют много общего в том, как они работают, на фундаментальном уровне они совершенно разные. Apple создала NSURLSession
чтобы напоминать общие концепции NSURLConnection
, но в ходе этой серии вы узнаете, что NSURLSession
является современным, простым в использовании и созданным с учетом требований мобильных устройств.
Что такое NSURLSession
?
Прежде чем обсуждать различия между NSURLSession
и NSURLConnection
, хорошей идеей будет сначала поближе познакомиться с NSURLSession
. Несмотря на название, NSURLSession
— это не просто еще один класс, который вы можете использовать в приложениях для iOS или OS X. NSURLSession
— это, прежде всего, такая же технология, как NSURLConnection
.
Сессии являются контейнерами
NSURLSession
и NSURLConnection
предоставляют API для взаимодействия с различными протоколами, такими как HTTP
и HTTPS
. Объект сеанса, экземпляр класса NSURLSession
, управляет этим взаимодействием. Это легко конфигурируемый контейнер с элегантным API, который обеспечивает детальное управление. Он предлагает функции, которые отсутствуют в NSURLConnection
. Более того, с NSURLSession
вы можете выполнять задачи, которые просто невозможны с NSURLConnection
, такие как реализация частного просмотра.
Задания
Основной единицей работы при работе с NSURLSession
является задача, экземпляр NSURLSessionTask
. Существует три типа задач: задачи с данными, задачи загрузки и задачи загрузки .
- Чаще всего вы будете использовать задачи с данными, которые являются экземплярами
NSURLSessionDataTask
. Задачи данных используются для запроса данных с сервера, таких как данные JSON. Принципиальное отличие задач выгрузки и загрузки заключается в том, что они возвращают данные непосредственно в ваше приложение, а не проходят через файловую систему. Данные хранятся только в памяти. - Как следует из названия, задачи загрузки используются для загрузки данных в удаленный пункт назначения.
NSURLSessionUploadTask
является подклассомNSURLSessionDataTask
и ведет себя аналогичным образом. Одно из ключевых отличий обычной задачи с данными заключается в том, что задачи загрузки можно использовать в сеансе, созданном в фоновой конфигурации сеанса. - Загрузите задачи, экземпляры
NSURLSessionDownloadTask
, наследуйте непосредственно отNSURLSessionTask
. Наиболее существенное отличие от задач с данными заключается в том, что задача загрузки записывает свой ответ непосредственно во временный файл. Это сильно отличается от обычной задачи данных, которая хранит ответ в памяти. Можно отменить задачу загрузки и возобновить ее позднее.
Как вы можете себе представить, асинхронность является ключевым понятием в NSURLSession
. API NSURLSession
возвращает данные, вызывая обработчик завершения или через делегата сеанса. API NSURLSession
был разработан с учетом гибкости, как вы заметите чуть позже в этом руководстве.
Встретить семью
Как я упоминал ранее, NSURLSession
— это и технология, и класс, с которым вы будете работать. NSURLSession
API содержит несколько классов, но NSURLSession
является ключевым компонентом, отправляющим запросы и получающим ответы. Однако конфигурация объекта сеанса обрабатывается экземпляром класса NSURLSessionConfiguration
. Класс NSURLSessionTask
и его три конкретных подкласса являются рабочими и всегда используются вместе с сеансом, поскольку именно сеанс создает объекты задачи.
Делегация
И NSURLSession
и NSURLConnection
сильно зависят от шаблона делегирования. Протокол NSURLSessionDelegate
объявляет несколько методов делегатов для обработки событий на уровне сеанса. Кроме того, NSURLSessionTask
класс NSURLSessionTask
и подклассы объявляют протокол делегата для обработки событий уровня задачи.
Старые друзья
API NSURLSession
на классах, с которыми вы уже знакомы, таких как NSURL
, NSURLRequest
и NSURLResponse
.
В чем различия?
Чем NSURLSession
отличается от NSURLConnection
? Это важный вопрос, потому что NSURLConnection
не осуждается Apple. Вы все еще можете использовать NSURLConnection
в своих проектах. Почему вы должны использовать NSURLSession
?
Первое, что нужно понять, это то, что экземпляр NSURLSession
является объектом, который управляет запросом и ответом. Это похоже на то, как работает NSURLConnection
, но ключевое отличие состоит в том, что конфигурация запроса обрабатывается объектом сеанса, который является долгоживущим объектом. Это делается через класс NSURLSessionConfiguration
. Он не только предоставляет NSURLSession
API NSURLSession
через класс NSURLSessionConfiguration
, но и способствует отделению данных (тела запроса) от метаданных. NSURLSessionDownloadTask
хорошо иллюстрирует это, напрямую записывая ответ в файловую систему.
Аутентификация проще и элегантнее обрабатывается NSURLSession
. API NSURLSession
обрабатывает аутентификацию на основе соединения, а не на основе запроса, как NSURLConnection
. NSURLSession
API также делает более удобным предоставление опций HTTP, и каждый сеанс может иметь отдельный контейнер хранения в зависимости от того, как вы настроили сеанс.
Во введении я говорил вам, что NSURLSession
предоставляет современный интерфейс, который изящно интегрируется с iOS 7. Одним из примеров такой интеграции является NSURLSession
вне процесса NSURLSession
. NSURLSession
оптимизирован для сохранения времени работы от батареи, поддерживает приостановку, отмену и возобновление задач, а также многозадачный API UIKit. Что не нравится в NSURLSession
?
Мокрые ноги
Шаг 1: Настройка проекта
Новый API лучше всего изучается на практике, поэтому пришло время запустить Xcode и намочить ноги. Запустите Xcode 5, создайте новый проект, выбрав New> Project … в меню File , и выберите шаблон приложения Single View Application из списка шаблонов приложений iOS.
Дайте вашему проекту имя, сообщите Xcode, где вы хотите его сохранить, и нажмите « Создать» . Нет необходимости ставить проект под контроль версий.
Шаг 2. Создание объекта сеанса
При работе с NSURLSession
важно понимать, что объект сеанса, экземпляр NSURLSession
, является звездным игроком. Он обрабатывает запросы и ответы, настраивает запросы, управляет хранением и состоянием сеанса и т. Д. Создать сеанс можно несколькими способами. Самый быстрый способ начать работу — это использовать метод класса sharedSession
как показано ниже.
1
2
3
4
5
|
— (void)viewDidLoad {
[super viewDidLoad];
NSURLSession *session = [NSURLSession sharedSession];
}
|
Создайте объект session
в методе viewDidLoad
контроллера представления, как показано выше. Созданный нами объект session
подходит для нашего примера, но в большинстве случаев вам, вероятно, потребуется немного больше гибкости. Объект session
мы только что создали, использует глобальные NSURLCache
, NSHTTPCookieStorage
и NSURLCredentialStorage
. Это означает, что он работает очень похоже на стандартную реализацию NSURLConnection
.
Шаг 3. Создание задачи с данными
Чтобы использовать объект session
, давайте запросим API поиска iTunes Store и поищем программное обеспечение, созданное Apple. API поиска iTunes Store прост в использовании и не требует аутентификации, что делает его идеальным для нашего примера.
Чтобы запросить API поиска, нам нужно отправить запрос на https://itunes.apple.com/search
и передать несколько параметров. Как мы видели ранее, при использовании NSURLSession
API запрос представляется задачей. Чтобы запросить API поиска, все, что нам нужно, это задача с данными, экземпляр класса NSURLSessionDataTask
. Взгляните на обновленную реализацию viewDidLoad
показанную ниже.
1
2
3
4
5
6
7
8
9
|
— (void)viewDidLoad {
[super viewDidLoad];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:@»https://itunes.apple.com/search?term=apple&media=software»] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(@»%@», json);
}];
}
|
Существует несколько методов, доступных для создания задачи, но ключевая концепция, которую необходимо понять, заключается в том, что объект session
выполняет фактическое создание и настройку задачи. В этом примере мы вызываем dataTaskWithURL:completionHandler:
и передаем ему экземпляр NSURL
а также обработчик завершения. Обработчик завершения принимает три аргумента: необработанные данные ответа ( NSData
), объект ответа ( NSURLResponse
) и объект ошибки ( NSError
). Если запрос выполнен успешно, объект ошибки имеет значение nil
. Поскольку мы знаем, что запрос возвращает ответ JSON, мы создаем объект Foundation из data
объекта data
и записываем результаты в консоль.
Важно понимать, что объект error
переданный обработчику завершения, заполняется, а не nil
, если запрос не выполнен или обнаружена ошибка. Другими словами, если запрос возвратил ответ 404
, запрос действительно был успешным в отношении сеансов. Объект error
будет равен nil
. Это важная концепция для понимания при работе с NSURLSession
и NSURLConnection
по этому вопросу.
Создайте проект и запустите приложение в iOS Simulator или на физическом устройстве и осмотрите консоль Xcode. Ничего не выводится на консоль. Что пошло не так? Как я упоминал ранее, NSURLSession
API поддерживает приостановку, отмену и возобновление задач или запросов. Это поведение аналогично NSOperation
и может напоминать вам библиотеку AFNetworking . Задача имеет свойство state
которое указывает, выполняется ли задача ( NSURLSessionTaskStateRunning
), приостановлена ( NSURLSessionTaskStateSuspended
), NSURLSessionTaskStateCanceling
( NSURLSessionTaskStateCanceling
) или выполнена ( NSURLSessionTaskStateCompleted
). Когда объект сеанса создает задачу, задача начинает свою жизнь в приостановленном состоянии. Чтобы запустить задачу, нам нужно сказать, чтобы она resume
, вызывая ее. Обновите метод viewDidLoad
как показано ниже, еще раз запустите приложение и проверьте вывод в консоли. Миссия выполнена.
01
02
03
04
05
06
07
08
09
10
11
|
— (void)viewDidLoad {
[super viewDidLoad];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:@»https://itunes.apple.com/search?term=apple&media=software»] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(@»%@», json);
}];
[dataTask resume];
}
|
Загрузка удаленного ресурса
В предыдущем примере мы использовали обработчик завершения для обработки ответа, полученного нами от запроса. Также можно достичь того же результата, внедрив протокол (ы) делегата задачи. Давайте посмотрим, что нужно для загрузки изображения, используя NSURLSession
и NSURLSessionDownloadTask
.
Шаг 1. Создайте пользовательский интерфейс
Откройте MTViewController.h
и создайте два выхода, как показано ниже. Мы будем использовать первый выход, экземпляр UIImageView
, для отображения загруженного изображения пользователю. Второй выпуск, экземпляр UIProgressView
, будет отображать ход выполнения задачи загрузки.
1
2
3
4
5
6
7
8
|
#import <UIKit/UIKit.h>
@interface MTViewController : UIViewController
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property (weak, nonatomic) IBOutlet UIProgressView *progressView;
@end
|
Откройте основную раскадровку проекта ( Main.storyboard ), перетащите экземпляр UIImageView
в представление контроллера представления и подключите выход контроллера представления, который мы только что создали, в заголовочном файле контроллера представления. Повторите этот процесс для просмотра прогресса.
Шаг 2. Создание задачи загрузки
В этом примере мы не будем использовать sharedSession
класса sharedSession
, потому что нам нужно настроить объект session
который мы будем использовать для выполнения запроса. Обновите реализацию viewDidLoad
как показано ниже.
1
2
3
4
5
6
7
8
|
— (void)viewDidLoad {
[super viewDidLoad];
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:nil];
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:[NSURL URLWithString:@»http://cdn.tutsplus.com/mobile/uploads/2013/12/sample.jpg»]];
[downloadTask resume];
}
|
Чтобы предотвратить появление предупреждений компилятора, убедитесь, что класс NSURLSessionDelegate
NSURLSessionDownloadDelegate
протоколам NSURLSessionDelegate
и NSURLSessionDownloadDelegate
, как показано ниже.
1
2
3
4
5
|
#import «MTViewController.h»
@interface MTViewController () <NSURLSessionDelegate, NSURLSessionDownloadDelegate>
@end
|
В viewDidLoad
мы создаем экземпляр класса NSURLSessionConfiguration
, вызывая defaultSessionConfiguration
класса defaultSessionConfiguration
. Как указано в документации, при использовании конфигурации сеанса по умолчанию сеанс будет вести себя как экземпляр NSURLConnection
в конфигурации по умолчанию, что хорошо для нашего примера.
В этом примере мы создаем экземпляр NSURLSession
, вызывая метод sessionWithConfiguration:delegate:delegateQueue:
метод класса, и передаем объект sessionWithConfiguration:delegate:delegateQueue:
созданный нами недавно. Мы устанавливаем контроллер представления в качестве делегата сеанса и передаем nil
в качестве третьего аргумента. Вы можете игнорировать третий аргумент на данный момент. Основное отличие от предыдущего примера состоит в том, что мы устанавливаем делегат session
в контроллере представления.
Чтобы загрузить изображение, нам нужно создать задачу загрузки. Мы делаем это, вызывая downloadTaskWithURL:
для объекта session
, передавая экземпляр NSURL
и вызывая resume
в задаче загрузки. Мы могли бы использовать обработчик завершения, как мы делали ранее, но я хочу показать вам возможности использования делегата.
Шаг 3: Реализация протокола делегата
Чтобы все это работало, нам нужно реализовать три метода- NSURLSessionDownloadDelegate
протокола NSURLSessionDownloadDelegate
, URLSession:downloadTask:didFinishDownloadingToURL:
URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:
и URLSession:downloadTask:downloadTask didWriteData:totalBytesWritten:totalBytesExpectedToWrite:
Реализация каждого метода довольно проста. Важно отметить, что нам нужно обновить пользовательский интерфейс в главном потоке, используя GCD (Grand Central Dispatch). sessionWithConfiguration:delegate:delegateQueue:
nil
в качестве третьего аргумента sessionWithConfiguration:delegate:delegateQueue:
операционная система создала для нас фоновую очередь. Это хорошо, но это также означает, что мы должны знать, что методы делегата вызываются в фоновом потоке вместо основного потока. Создайте проект и запустите приложение, чтобы увидеть результат нашей тяжелой работы.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
— (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
NSData *data = [NSData dataWithContentsOfURL:location];
dispatch_async(dispatch_get_main_queue(), ^{
[self.progressView setHidden:YES];
[self.imageView setImage:[UIImage imageWithData:data]];
});
}
— (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes {
}
— (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
float progress = (double)totalBytesWritten / (double)totalBytesExpectedToWrite;
dispatch_async(dispatch_get_main_queue(), ^{
[self.progressView setProgress:progress];
});
}
|
Вывод
С этими двумя примерами вы должны иметь NSURLSession
представление об NSURLSession
API NSURLSession
, о том, как он сравнивается с NSURLConnection
, и каковы его преимущества. В следующей части этой серии мы рассмотрим более продвинутые функции NSURLSession
.