Статьи

Постоянство данных и песочница на iOS

Сохранение данных при запуске приложений — это требование, которое есть в большинстве приложений iOS: от сохранения пользовательских настроек с использованием системы пользовательских настроек по умолчанию до управления большими наборами данных в реляционной базе данных. В этой статье мы рассмотрим наиболее распространенные стратегии, используемые для хранения данных в приложении iOS. Я также расскажу о файловой системе iOS и о том, как песочница в приложениях влияет на сохранность данных.


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

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


Безопасность на платформе iOS была одним из главных приоритетов Apple с тех пор, как iPhone был представлен в 2007 году. В отличие от приложений OS X, приложение iOS находится в изолированной программной среде. В отличие от того, что думает большинство людей, песочница приложения не только ссылается на каталог песочницы приложения в файловой системе. Тестовая среда приложения также включает в себя контролируемый и ограниченный доступ к пользовательским данным, хранящимся на устройстве, системным службам и оборудованию.

С появлением Mac App Store Apple начала применять изолированную программную среду приложений в OS X. Хотя ограничения, накладываемые на приложения OS X, не такие строгие, как ограничения, накладываемые на приложения iOS, общая концепция аналогична, но не идентична. Например, в изолированной программной среде приложения для iOS содержится пакет приложений, чего нельзя сказать о приложениях OS X. Причины этих различий в основном исторические.

Операционная система устанавливает каждое приложение iOS в каталог с песочницей, который содержит каталог пакета приложений и три дополнительных каталога, Documents , Library и tmp . Доступ к каталогу песочницы приложения, часто называемому его домашним каталогом, можно получить, вызвав простую функцию Foundation, NSHomeDirectory() .

1
NSLog(@»HOME > %@», NSHomeDirectory());

Вы можете попробовать это сами. Создайте новый проект Xcode на основе шаблона приложения Single View и назовите его « Постоянство данных».

Откройте TSPAppDelegate.m и добавьте приведенный выше фрагмент кода в application:didFinishLaunchingWithOptions:

Если вы запустите приложение в iOS Simulator, вывод в консоли будет выглядеть примерно так, как показано ниже.

1
2014-03-27 09:48:16.794 Data Persistence[1426:60b] HOME > /Users/Bart/Library/Application Support/iPhone Simulator/7.1/Applications/5024403A-C65E-44DD-BCD2-F93097FB502E

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

1
2014-03-27 09:48:51.571 Data Persistence[1426:60b] HOME > /var/mobile/Applications/A4D17A73-84D7-4628-9E32-AEFEA5EE6153

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

1
2
3
NSArray *directories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documents = [directories firstObject];
NSLog(@»DOCUMENTS > %@», documents);

Мы используем NSSearchPathForDirectoriesInDomains() и NSDocumentDirectory константу NSDocumentDirectory в качестве первого аргумента, чтобы указать, что нас интересует только каталог документов приложения. Второй и третий аргументы имеют меньшее значение для этого обсуждения. Функция возвращает экземпляр NSArray содержащий один результат, путь к каталогу документов приложения.

Вы можете быть удивлены, почему я использую firstObject вместо objectAtIndex: для выборки первого и единственного объекта в массиве путей. Несмотря на то, что я могу быть уверен, что возвращаемый массив не является пустым, если массив будет пустым и массив получит сообщение objectAtIndex: с аргументом 0 , приложение будет аварийно завершено из-за необработанного исключения.

Однако, вызывая firstObject для массива, массив возвращает nil если он не содержит никаких объектов, что означает, что исключение не будет выдано. Помните, документация — ваш друг.

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

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

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

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

Несмотря на то, что вы можете делать практически все, что захотите, в песочнице своего приложения, Apple предоставила несколько рекомендаций относительно того, что и где хранить. Об этих рекомендациях важно знать по нескольким причинам. Когда iTunes создает резервную копию устройства iOS, не все файлы в изолированной программной среде приложения включаются в резервную копию.

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

Каталог Documents предназначен для пользовательских данных, а каталог Library — для данных приложения, которые не привязаны строго к пользователю. Каталог Caches в каталоге Library — это еще один каталог, который не резервируется iTunes.

Также имейте в виду, что ваше приложение не должно изменять содержимое каталога комплекта приложений. Каталог комплекта приложений подписывается при установке приложения. Изменяя содержимое каталога комплекта приложений любым способом, вышеупомянутая подпись изменяется, что означает, что операционная система не позволяет приложению снова запускаться. Это еще одна мера безопасности, введенная Apple для защиты потребителей.


Существует несколько стратегий хранения данных приложения на диске. В этой статье мы кратко рассмотрим четыре распространенных подхода на iOS:

  • пользовательские настройки по умолчанию
  • списки свойств
  • SQLite
  • Основные данные

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


Система по умолчанию пользователя — это то, что iOS унаследовала от OS X. Несмотря на то, что она была создана и предназначена для хранения пользовательских настроек, ее можно использовать для хранения любого типа данных, если это тип списка свойств, NSString , NSNumber , NSDate , NSArray , NSDictionary и NSData или любой из их изменяемых вариантов.

База данных пользователя по умолчанию — это не что иное, как набор списков свойств, по одному списку свойств на приложение. Список свойств хранится в папке « Preferences» в папке « Library » приложения, которая указывает на назначение и функцию списка свойств.

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

01
02
03
04
05
06
07
08
09
10
11
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
 
[userDefaults setBool:YES forKey:@»Key1″];
[userDefaults setInteger:123 forKey:@»Key2″];
[userDefaults setObject:@»Some Object» forKey:@»Key3″];
 
[userDefaults boolForKey:@»Key1″];
[userDefaults integerForKey:@»Key2″];
[userDefaults objectForKey:@»Key3″];
 
[userDefaults synchronize];

При вызове метода класса standardUserDefaults в NSUserDefaults ссылка на объект общих значений по умолчанию.

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

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

Прежде чем двигаться дальше, вставьте приведенный выше фрагмент кода в приложение делегата application:didFinishLaunchingWithOptions: метод и запустите приложение в iOS Simulator. Откройте новое окно Finder и перейдите к « Библиотека»> «Поддержка приложений»> «iPhone Simulator»> «7.1»> «Приложения» (замените). «7.1» с последней версией iOS).

Найдите папку приложения, соответствующую приложению, проверив различные зашифрованные папки в папке « Приложения ». Папка с загадочным именем на самом деле является каталогом песочницы приложения. В каталоге изолированной программной среды приложения откройте папку « Установки », расположенную в папке « Библиотека », и проверьте ее содержимое.

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

Если вы хотите упростить доступ к песочнице приложения в iOS Simulator, я настоятельно рекомендую вам взглянуть на SimPholder . Это бесплатная утилита, которая значительно облегчает работу с симулятором iOS.


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

Как я упоминал ранее, важно иметь в виду, что список свойств может хранить только данные списка свойств. Означает ли это, что невозможно сохранить объекты пользовательской модели, используя списки свойств? Нет, это возможно. Однако, объекты пользовательской модели должны быть заархивированы — форма сериализации — прежде чем они могут быть сохранены в списке свойств. Архивация объекта просто означает, что объект необходимо преобразовать в тип данных, который можно сохранить в списке свойств, например в экземпляре NSData .

Вы помните протокол NSCoding , определенный в платформе Foundation? Протокол NSCoding определяет два метода, initWithCoder: и encodeWithCoder: которые должен реализовать класс, чтобы обеспечить возможность кодирования и декодирования экземпляров класса.

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

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
NSArray *fruits = @[@»Apple», @»Mango», @»Pineapple», @»Plum», @»Apricot»];
 
NSString *filePathFruits = [documents stringByAppendingPathComponent:@»fruits.plist»];
[fruits writeToFile:filePathFruits atomically:YES];
 
NSDictionary *miscDictionary = @{@»anArray» : fruits, @»aNumber» : @12345, @»aBoolean» : @YES};
 
NSString *filePathDictionary = [documents stringByAppendingPathComponent:@»misc-dictionary.plist»];
[miscDictionary writeToFile:filePathDictionary atomically:YES];
 
NSArray *loadedFruits = [NSArray arrayWithContentsOfFile:filePathFruits];
NSLog(@»Fruits Array > %@», loadedFruits);
 
NSDictionary *loadedMiscDictionary = [NSDictionary dictionaryWithContentsOfFile:filePathDictionary];
NSLog(@»Misc Dictionary > %@», loadedMiscDictionary);

Давайте посмотрим на приведенный выше фрагмент кода. Мы начнем с сохранения ссылки на литерал массива в переменной с именем fruits . Мы создаем путь к файлу для хранения списка свойств, который мы собираемся сделать. Путь к файлу создается путем добавления строки к пути к файлу каталога Documents , который мы получили ранее в этом уроке. Добавляемая строка будет именем списка свойств, включая его расширение, .plist , которое мы создадим за секунду.

writeToFile:atomically: массив на диск так же просто, как вызвать writeToFile:atomically: для массива. Вы можете пока игнорировать atomically флаг.

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

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


Если ваше приложение управляется данными и работает с большими объемами данных, вы можете захотеть изучить SQLite. Что такое SQLite? Слоган на веб-сайте SQLite гласит: «Маленький. Быстрый. Надежный. Выберите любые три». Это прекрасно подводит итог.

SQLite — это библиотека, которая реализует облегченную встроенную реляционную базу данных. Как следует из его названия, он основан на стандарте SQL (язык структурированных запросов ), как и MySQL и PostgreSQL.

Основное отличие от других баз данных SQL состоит в том, что SQLite является переносимым, очень легковесным и что он не требует сервера, а не является отдельным процессом, доступным из клиентского приложения. Другими словами, это встроено в приложение и, следовательно, очень быстро.

Веб-сайт SQLite утверждает, что это самая распространенная база данных SQL. Я не знаю, так ли это до сих пор, но это, безусловно, популярный выбор для хранения данных на стороне клиента. Aperture и iPhoto, например, полагаются на SQLite для хранения некоторых своих данных.

Преимущество SQLite перед работой непосредственно с объектами состоит в том, что SQLite работает на несколько порядков быстрее, что в значительной степени связано с тем, насколько существенно отличаются реляционные базы данных и языки объектно-ориентированного программирования.

Чтобы преодолеть разрыв между SQLite и Objective-C, с течением времени было создано несколько решений Object Relational Mapping (ORM). ORM, созданный Apple для iOS и OS X, называется Core Data , и мы рассмотрим его позже в этом уроке.

Использование SQLite на iOS означает работу с библиотекой на Си. Если вы предпочитаете объектно-ориентированное решение, я настоятельно рекомендую оболочку Objective-C Гуса Мюллера ( Flying Meat, Inc. ) для SQLite, FMDB .

Это делает работу с SQLite намного проще, если вы предпочитаете объектно-ориентированный интерфейс. Библиотека поддерживает ARC ( автоматический подсчет ссылок ) из коробки и очень эффективна. Я использовал FMDB в прошлом и был очень доволен его API, надежностью и надежностью библиотеки.


Разработчики, плохо знакомые с Core Data, часто ошибочно принимают Core Data за базу данных, хотя это действительно решение для реляционного отображения объектов, созданное и поддерживаемое Apple. Мэтт Галлахер написал отличный пост о различиях между Core Data и базой данных. Базовые данные предоставляют реляционную объектно-ориентированную модель, которую можно сериализовать в хранилище XML, двоичное или SQLite. Базовые данные даже поддерживают хранилище в памяти.

Почему вы должны использовать Core Data вместо SQLite? Задавая этот вопрос, вы ошибочно полагаете, что Core Data — это база данных. Преимущество использования Core Data заключается в том, что вы работаете с объектами, а не с необработанными данными, такими как строки в базе данных SQLite или данные, хранящиеся в файле XML. Несмотря на то, что у Core Data было несколько трудных лет, когда она была впервые выпущена, она превратилась в надежную среду с множеством функций, таких как автоматические миграции, отслеживание изменений, сбои и встроенная проверка.

Еще одна замечательная особенность, которую многие разработчики ценят, — это редактор модели Core Data, встроенный в Xcode, который позволяет разработчикам моделировать свою модель данных через графический интерфейс.

Является ли Core Data правильным решением для вашего приложения, зависит от данных, которыми вы планируете управлять, как с точки зрения количества, так и базовой модели. Если вы планируете управлять очень большими наборами данных, то базовые данные со временем могут стать узким местом для производительности. В этом случае SQLite может быть лучшим решением.


Вы, наверное, слышали об iCloud, и вам может быть интересно, где iCloud вписывается в историю сохранения данных. iCloud не является такой формой сохранения данных, как SQLite или Core Data. Вместо этого это платформа или сервис для предоставления пользовательских данных на нескольких устройствах и нескольких экземплярах приложения или даже семейства приложений.

Платформа iCloud включает в себя несколько сервисов или компонентов. Интересующий нас компонент — iCloud Storage , который включает в себя три типа хранилищ:

  • хранилище ключей
  • хранение документов
  • Базовое хранение данных.

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

Теперь у вас должно быть хорошее представление о возможных вариантах сохранения данных при разработке для платформы iOS. Имейте в виду, что не все стратегии, которые мы рассмотрели, равны.

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