Сетевое кеширование в iOS. NSURLCache

от автора

Данная тема важна при разработке любого приложения, взаимодействующего с сетью. Здесь грамотное использование возможностей системы может значительно улучшить взаимодействие пользователя с программой.

NSURLCache — это комплексное решение для кеширования сетевых запросов в оперативной памяти или на диске. В соответствии с документацией Apple, любой запрос с использованием NSURLConnection будет «пропущен» через NSURLCache.

Кеширование уменьшает количество необходимых обращений к сети, улучшает впечатление от работы с программой во время полного отсутствия интернета или проблем с сетевым соединением.

После загрузки ответа сервера, его копия сохраняется в локальном кеше. В следующий раз при посылке такого же запроса, ответ будет возвращен мгновенно, без обращения к сети. NSURLCache прозрачным для пользователя образом вернет закешированные данные.

Для использования NSURLCache нужно установить значение синглтона sharedURLCache. Это можно сделать в методе application:didFinishLaunchingWithOptions: на iOS или в applicationDidFinishLaunching: — на Mac OS X:

-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions  {     NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024                                                           diskCapacity:20 * 1024 * 1024                                                               diskPath:nil];     [NSURLCache setSharedURLCache:URLCache]; } 

Вы можете управлять настройками кеширования — как на стороне клиента, так и на стороне сервера. Грамотный выбор опций может быть полезен при оптимизации вашего приложения.

NSURLRequestCachePolicy

На стороне запроса к серверу политика кеширования задается с помощью свойства cachePolicy объекта NSURLRequest. Возможен выбор одного из следующих вариантов:

  • NSURLRequestUseProtocolCachePolicy — значение по умолчанию. Логика кеширования определяется реализацией сетевого протокола, используемого в запросе.
  • NSURLRequestReloadIgnoringLocalCacheData — данные всегда грузятся с сервера, содержимое кеша полностью игнорируется.
  • NSURLRequestReloadIgnoringLocalAndRemoteCacheData — содержимое локального кеша игнорируется. Помимо этого, прокси-сервера и другая промежуточная инфраструктура должна быть проинструктирована не использовать по возможности закешированную копию данных.
  • NSURLRequestReturnCacheDataElseLoad — информация возвращается из кеша, при этом сведения о ее актуальности не учитываются. Если данные в кеше отсутствуют — они грузятся из сети.
  • NSURLRequestReturnCacheDataDontLoad — данные берутся из кеша, сведения об их устаревании игнорируются. Однако, если сохраненная информация отсутствует — запрос сразу считается не прошедшим, без попытки получить ее с сервера.
  • NSURLRequestReloadRevalidatingCacheData — закешированные данные используются только после предварительного подтверждения их валидности сервером. Ставшие неактуальными данные — выкачиваются из сети.

Различия между этими вариантами не всегда очевидны и часто приводят к путанице. Не добавляет ясности и тот факт, что NSURLRequestReloadIgnoringLocalAndRemoteCacheData и NSURLRequestReloadRevalidatingCacheData — в принципе не реализованы.

То, что действительно нужно знать о NSURLRequestCachePolicy можно резюмировать следующим образом:

  • UseProtocolCachePolicy — поведение по умолчанию
  • ReloadIgnoringLocalCacheData — не использовать кеш
  • ReloadIgnoringLocalAndRemoteCacheData — не использовать кеш. Совсем
  • ReturnCacheDataElseLoad — использовать кеш. Игнорировать информацию о его актуальности
  • ReturnCacheDataDontLoad — оффлайн-режим. Использовать только закешированные данные, независимо от их «свежести»
  • ReloadRevalidatingCacheData — перед использованием спросить у сервера, насколько кеш актуален

Кеширование в HTTP

Поскольку класс NSURLConnection предназначен для работы с различными сетевыми протоколами (включая FTP и HTTP/HTTPS) в документации кеширование описано протоколо-независимым образом. В данной статье мы будем рассматривать его с точки зрения протокола HTTP.

В HTTP для обмена метаинформацией о кодировках, MIME-типах, кешировании итп используются заголовки запросов и ответов сервера.

Заголовки запроса

По умолчанию NSURLRequest использует текущее время для того, чтобы определить, нужно ли возвращать закешированную информацию. Для более тонкой настройки можно использовать следующие заголовки:

  • If-Modified-Since — этот заголовок соответствует заголовку Last-Modified ответа от сервера. Его значение нужно установить в Last-Modified из последнего обращения к данному сервису.
  • If-None-Match — соответствует Etag-заголовку ответа. Сюда нужно передать предыдущее полученное значение Etag.

Заголовки ответа

С NSHTTPURLResponse также могут быть возвращены заголовки, относящиеся к кешированию:

  • Cache-Control — этот заголовок должен присутствовать в ответе, чтобы включить HTTP-кеширование на клиенте. Его значение может содержать информацию о длительности хранения данных в кеше, а также об уровне доступа к ним. Подробная информация доступна здесь.

Помимо Cache-Control, сервер может прислать дополнительные заголовки, использующиеся для условного получения данных (см. предыдущую секцию):

  • Last-Modified — время последнего изменения запрашиваемого ресурса. Например при получении информации о фотоальбоме заголовок Last-Modified может быть установлен в значение, эквивалентное дате последнего фото.
  • Etag — идентификатор содержимого требуемого объекта. На практике это может быть, например, MD5-хэш состояния ресурса. Это полезно в случае динамически генерирующихся данных, для которых определение Last-Modified проблематично.

NSURLConnectionDelegate

При обработке результата запроса делегат NSURLConnection имеет возможность изменить закешированный ответ с помощью метода connection:willCacheResponse:. В этот вызов передается объект NSCachedURLResponse, который содержит ссылку на исходный NSURLResponse и закешированные данные в виде NSData. Этот объект создается на основе информации, полученной из сетевого соединения. Поскольку вы не можете изменять экземпляры класса NSCachedURLResponse, для редактирования каких-либо параметров нужно создать новый объект с помощью инициализатора initWithResponse:data:userInfo:storagePolicy:, например:

-(NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {     NSMutableDictionary *mutableUserInfo = [[cachedResponse userInfo] mutableCopy];     NSMutableData *mutableData = [[cachedResponse data] mutableCopy];     NSURLCacheStoragePolicy storagePolicy = NSURLCacheStorageAllowedInMemoryOnly;      // ...      return [[NSCachedURLResponse alloc] initWithResponse:[cachedResponse response]                                                      data:mutableData                                                  userInfo:mutableUserInfo                                            storagePolicy:storagePolicy]; } 

Если метод connection:willCacheResponse: вернет nil — ответ не будет закеширован вообще.

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {     return nil; } 

Метод connection:willCacheResponse: — опциональный. Если он не реализован в делегате, будет использован автоматически созданный экземпляр NSCachedURLResponse.

Подводные камни

При работе с NSURLCache есть несколько особенностей:

Заключение

Пример NSURLCache в очередной раз показывает нам, насколько важно знать возможности системы, с которой работаешь.

Многие разработчики изобретают свой велосипед для организации кеширования, так как не знают о возможностях NSURLCache, инициализация которого занимает всего 2 строчки кода и делает работу в 100 раз эффективнее. Еще большее число вообще не задумывается о преимуществах сетевого кеширования и не использует его, нагружая свой сервер огромным количеством ненужных запросов.

Таким образом, для оптимальной работы своего приложения всегда инициализируйте NSURLCache в методе application:didFinishLaunchingWithOptions:.

ссылка на оригинал статьи http://habrahabr.ru/post/179349/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *