Сетевое кэширование в iOS. Введение

в 11:50, , рубрики: ios development, mobile development, Блог компании e-Legion Ltd., разработка под iOS, метки:

Практически каждое мобильное приложение получает какие-либо данные из сети.
К сожалению, доступ к сети не всегда возможен и поэтому разработчику важно правильно реализовать сетевой кэш в приложении.

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

Итак, введение.

Стратегии для кэширования

Есть два подхода к кэшированию: кэширование по требованию и предварительное кэширование.

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

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

Чтобы определить какую стратегию кэширования использовать, нужно понять, может ли потребоваться пост обработка данных после их загрузки. Пост обработка подразумевает какую-либо модификацию загруженных данных. Например, изменение ссылок в HTML странице так, чтобы они указывали на картинки кэшированные локально и т.д.

Где хранить кэш

Приложения могут хранить информацию только в своей песочнице. Так как кэшируемые данные не генерируются пользователем, то они должны быть сохранены в NSCachesDirectory, а не в NSDocumentsDirectory. Хорошей практикой является создание отдельной директории, для всех кэшируемых данных.

В этом примере в папке Library/Caches создается директория MyAppCache:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachesDirectory = [paths objectAtIndex:0];
cachesDirectory = [cachesDirectory stringByAppendingPathComponent:@”MyAppCache”];

Причиной хранения кэша в папке Library/Caches является то, что iCloud (и iTunes) исключает эту директорию из бэкапа. И, следовательно, и так ограниченное в iCloud пространство (в настоящее время для бесплатного аккаунта, это около 5 GB) не тратится на хранение ненужных данных.

В случае если в приложении происходит интенсивное кэширование, рекомендуется вместо диска использовать память и выгружать данные на диск при закрытии приложения. Это связано с тем, что flash память iPhone имеет ограниченное число циклов записи/чтения и нежелательно нагружать ее лишний раз.

Как хранить кэш

На iOS существует множество различных способов хранения пользовательских данных. Для кэширования лучше всего подходят: NSKeyedArchiver, Core Data, SQLite, NSURLCache.

NSKeyedArchiver

Кэширование модели данных реализуется с использованием класса NSKeyedArchiver. Для того, чтобы объекты модели могли быть архивированы, классы модели должны реализовать протокол NSCoding. А именно методы

- (void)encodeWithCoder:(NSCoder *)aCoder;
- (id)initWithCoder:(NSCoder *)aDecoder;

Если класс реализует NSCoding, для архивации достаточно вызвать один из следующих методов:

[NSKeyedArchiver archiveRootObject:objectForArchiving toFile:archiveFilePath];

[NSKeyedArchiver archivedDataWithRootObject:objectForArchiving];

Первый метод создаст файл с архивом по пути archiveFilePath. Второй метод вернет объект NSData. NSData обычно быстрее, так как отсутствуют дополнительные затраты на доступ к файлу, но при этом данные будут храниться в памяти приложения.

Для разархивирования модели из файла (или указателя на NSData) используется класс NSKeyedUnarchiver. Разархивировать данные можно одним из следующих методов:

[NSKeyedUnarchiver unarchiveObjectWithData:data];

[NSKeyedUnarchiver unarchiveObjectWithFile:archiveFilePath];

Использование NSKeyedArchiver/NSKeyedUnarchiver требует чтобы модели удовлетворяли протоколу NSCoding. Реализация NSCoding очень проста, но если файлов много, то она может занять много времени. Поэтому для автоматизации этого процесса лучше использовать какой-либо инструмент. Например среду разработки AppCode.

Core Data

Чтобы хранить данные в Core Data, необходимо создать файл модели, который содержит описание сущностей (Entities), а также связей между ними (Relationships), и написать методы для сохранения и получения данных. Используя Core Data можно получить настоящий offline режим работы приложения, как это сделано в стандартных приложениях Mail и Calendar.

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

Несмотря на то, что Core Data можно использовать для кэширования по требованию, лучше этого не делать. Главное преимущество Core Data заключается в предоставление доступа к свойствам модели без необходимости разархивировать все данные. Однако сложность реализации Core Data в приложении, перекрывает это преимущество.

Raw SQLite

Для работы с SQLite надо слинковать приложение с библиотекой libsqlite3, но такой подход имеет значительные недостатки.
Все sqlite3 библиотеки и механизм Object Relational Mapping (ORM) работают медленнее чем Core Data. Кроме того реализация sqlite3 в iOS не потоко-безопасна. Так что если вы не используете отдельно собранную sqlite3 библиотеку (скомпилированную с флагом thread-safe), то вы сами отвечаете за то, чтобы гарантировать потоко-безопасный доступ на чтение/запись к базе данных sqlite3.

Так как Core Data может предложить гораздо больше возможностей (миграция данных, встроенная потоко-безопасность, ...) рекомендуется избегать использование нативной SQLite на iOS.

NSURLCache

Идеальный вариант для кэширования по запросу. Позволяет кэшировать данные возвращаемые NSURLRequest практически в автоматическом режиме и требует минимум кода.

Всего лишь пара строк и ваше приложение получит дисковый кэш для запросов.

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

К сожаление пригоден только для REST сервисов и есть проблемы при работе с некоторыми HTTP заголовками.

Заключение

Для реализации кэширования по запросу лучше использовать NSURLCache или NSKeyedArchiver. Реализация полноценного offline режима требует работы c CoreData.

В следующей части я планирую детально рассмотреть работу с NSURLCache/NSKeyedArchiver и описать случаи, для которых они подходят.

Автор: Fanruten

Источник

Поделиться

* - обязательные к заполнению поля