...

четверг, 17 октября 2013 г.

Сетевое кэширование в 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 и описать случаи, для которых они подходят.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:



Комментариев нет:

Отправить комментарий