SA1015 — Сохранение и загрузка (Saving & Loading)
Содержание страницы
Теперь у вас есть полная функциональность управления предметами, работающими для Checklists — вы можете добавлять предметы, редактировать их и даже удалять. Однако любые новые элементы, которые вы добавляете в список, перестают существовать при завершении работы приложения-например, при нажатии кнопки Stop в Xcode. И когда вы удаляете элементы из списка, они продолжают появляться после нового запуска. Это не то, как должно вести себя реальное приложение!
Итак, пришло время рассмотреть вопрос о персистентности данных(data persistence) — или, проще говоря, о сохранении и загрузке элементов…
В этой главе вы расскажете о следующем:
- Потребность в постоянстве данных (The need for data persistence):** краткий обзор того, зачем вам нужно постоянство данных.
- Папка документы (The documents folder): Определите, где в файловой системе можно разместить файл, в котором будут храниться элементы списка дел.
- Сохранение элементов контрольного списка (Save checklist items): сохранение элементов списка дел в файл всякий раз, когда пользователь вносит изменения, такие как добавление нового элемента, переключение флажка, удаление элемента и т. Д.
- Загрузить файл: Загрузите элементы задач из сохраненного файла,когда приложение снова запустится после завершения работы.
Необходимость сохранения данных
Благодаря многозадачности iOS приложение остается в памяти, когда вы закрываете его и возвращаетесь на главный экран или переключаетесь на другое приложение. Приложение переходит в приостановленное состояние (a suspended state), когда оно абсолютно ничего не делает и все же продолжает цепляться за свои данные.
При нормальном использовании пользователи никогда по-настоящему не завершат работу приложения, а просто приостановят ее. Тем не менее, приложение все еще может быть прекращено, когда iOS исчерпает доступную рабочую память, так как iOS завершит работу всех приостановленных приложений, чтобы освободить память, когда это необходимо. И если они действительно хотят, пользователи могут убивать приложения вручную или перезапускать/перезагружать все свое устройство.
Просто хранить список элементов в памяти недостаточно, потому что нет никакой гарантии, что приложение останется в памяти навсегда, будь то активное или приостановленное.
Вместо этого вам нужно будет сохранить эти данные в файле на долговременной флэш-памяти устройства. Это ничем не отличается от сохранения файла из текстового процессора на настольном компьютере, за исключением того, что приложения iOS должны делать это автоматически.
Пользователю не нужно нажимать кнопку Сохранения только для того, чтобы убедиться, что несохраненные данные надежно помещены в долгосрочное хранилище (long-term storage).

Приложения должны сохранять данные только на случай, если приложение будет закрыто
Так что давайте разберемся с этой функцией сохранения данных!
Папка с документами (The documents folder)
Приложения iOS живут в защищенной среде, известной как песочница (the sandbox). Каждое приложение имеет свою собственную папку для хранения файлов, но не может получить доступ к каталогам или файлам, принадлежащим любому другому приложению.
Это мера безопасности, предназначенная для предотвращения нанесения какого-либо ущерба вредоносными программами, такими как вирусы. Если приложение может изменять только свои собственные файлы, оно не может изменять (или влиять) на любую другую часть системы.
Ваши приложения могут хранить файлы в папке “Документы” в песочнице приложения.
Резервное копирование содержимого папки Documents выполняется при синхронизации устройства с iTunes или iCloud.
Когда вы выпускаете новую версию приложения и пользователи устанавливают обновление, папка Documents остается нетронутой. Все данные, сохраненные приложением в этой папке, остаются там при обновлении приложения.
Другими словами, ==папка Documents — это идеальное место для хранения файлов данных вашего пользователя==.
Получить путь к файлу сохранения (Get the save file path)
Давайте посмотрим, как это работает в коде.
- Добавьте следующие методы в ChecklistViewController.swift:
func documentsDirectory() -> URL {
let paths = FileManager.default.urls(
for: .documentDirectory,
in: .userDomainMask)
return paths[0]
}
func dataFilePath() -> URL {
return documentsDirectory().appendingPathComponent("Checklists.plist")
}
documentsDirectory()
возвращает полный путь к папке Documents.
Метод dataFilePath()
используется documentsDirectory()
для построения полного пути к файлу, в котором будут храниться элементы контрольного списка. Этот файл называется Checklists.plist и находится в папке Documents.
Обратите внимание, что оба метода возвращают URL
объект. iOS использует URL-адреса для ссылок на файлы в своей файловой системе. Там, где веб-сайты используют http://
URL-адреса или https://
URL-адреса, для ссылки на файл вы используете file://
URL-адрес.
Примечание: Дважды проверьте, чтобы убедиться, что ваш код говорит
.documentsDirectory
, а не.documentationDirectory
. Автозаполнение Xcode может легко сбить вас с толку здесь!
- Все еще в ChecklistViewController.swift добавьте следующие два
print
оператора в нижнюю частьviewDidLoad
:
override func viewDidLoad() {
. . .
items.append(item5)
// Add the following
print("Documents folder is \(documentsDirectory())")
print("Data file path is \(dataFilePath())")
}
- Запустите приложение. Консоль Xcode теперь покажет вам, где на самом деле находится папка документов вашего приложения.
Если я запускаю приложение из симулятора, в моей системе оно показывает что-то вроде этого:

Вывод консоли, показывающий расположение папок документов и файлов данных
Если вы запустите его на своем iPhone, путь будет выглядеть несколько иначе. Вот что говорит мой:
Documents folder is file:///var/mobile/Applications/FDD50B54-9383-4DCC-9C19-C3DEBC1A96FE/Documents
Data file path is file:///var/mobile/Applications/FDD50B54-9383-4DCC-9C19-C3DEBC1A96FE/Documents/Checklists.plist`
Как вы заметили, имя папки-это случайный 32-символьный идентификатор. Xcode выбирает этот идентификатор, когда устанавливает приложение на симулятор или устройство. Все, что находится внутри этой папки, является частью песочницы приложения.
Просмотр папки «Документы»
Для остальной части этого приложения запустите приложение на симуляторе, а не на устройстве. Это облегчает просмотр файлов, которые вы будете записывать в папку «Документы». Поскольку Симулятор хранит файлы приложения в обычной папке на вашем Mac, вы можете легко просмотреть их с помощью Finder.
- Откройте новое окно Finder, нажав на рабочий стол и набрав ⌘+N. Или, щелкнув значок Finder в док-станции, если она у вас есть. Затем нажмите ⌘+Shift+G — или выберите Go ▸ Перейти в папку… из меню, скопируйте путь к папке Documents из консоли Xcode и вставьте полный путь к папке Documents в диалоговом окне. (Не включайте file://. Путь начинается с **/Users/yourname/…)
- В этот момент вы, вероятно, увидите пустую папку. Чтобы перейти на один уровень вверх в Finder и увидеть папку песочницы для приложения, используйте комбинацию клавиш ⌘+↑ (command + стрелка вверх) или опцию меню Go ▸ Enclosing Folder
Держите это окно открытым, чтобы вы могли убедиться, что файл Checklists.plist действительно создан, когда вы доберетесь до этой части.

Структура каталогов приложения в симуляторе
Совет: Если вы хотите перейти к каталогу приложений симулятора, пройдя через структуру папок, то вы должны знать, что папка библиотеки, которая находится в вашей домашней папке, обычно скрыта. Если вы не видите папку Библиотеки (the Library folder), удерживайте нажатой клавишу Alt/Option и нажмите меню Go Finder или удерживайте нажатой клавишу Alt, пока открыто меню Go. Это должно открыть ярлык папки библиотеки в меню Go, если он не был виден ранее.
Вы можете увидеть несколько папок внутри папки песочницы приложения:
- Папка «Документы» (Documents), в которую приложение будет помещать свои файлы данных. В данный момент папка «Документы» пуста.
- В папке Библиотеки (Library) есть файлы кэша и файлы настроек. Содержимое этой папки управляется операционной системой.
- Папка systemData, как следует из названия, предназначена для использования операционной системой для хранения любой информации системного уровня, относящейся к приложению.
- Папка tmp предназначена для временных файлов. Иногда приложениям необходимо создавать файлы для временного использования. Вы не хотите, чтобы они загромождали вашу папку документов, поэтому tmp-хорошее место для их размещения. iOS будет время от времени очищать эту папку.
Также можно получить обзор папки «Документы» приложений на вашем устройстве.
- На устройстве перейдите в Настройки ▸ Общие ▸ Хранилище iPhone (Settings ▸ General ▸ iPhone Storage), прокрутите вниз до списка установленных приложений — возможно, вам придется подождать загрузки списка — и нажмите на название приложения.
Теперь вы увидите размер содержимого папки «Документы», но не само содержимое:

Просмотр информации о папке «Документы» на устройстве
Сохранить элементы контрольного списка (Save checklist items)
В этом разделе вы напишете код, который сохраняет список дел в файл с именем Checklists.plist, когда пользователь добавляет новый элемент или редактирует существующий элемент. Как только вы сможете сохранить элементы, вы добавите код для повторной загрузки этого списка при запуске приложения.
Файлы Plist
Итак, что же такое файл .plist?
Вы уже видели файл с именем Info.plist на уроке «Яблочко». У всех приложений есть один, включая приложение Checklists — см. Навигатор проекта. Info.plist содержит несколько параметров конфигурации, которые дают iOS дополнительную информацию о приложении, например, имя, отображаемое под значком приложения на главном экране.
“plist” расшифровывается как — Список свойств и представляет собой формат XML-файла, в котором хранятся структурированные данные, обычно в виде списка настроек и их значений. Файлы списка свойств очень распространены в iOS. Они подходят для многих типов хранения данных, и самое главное, они просты в использовании. Что может не нравиться?
Чтобы сохранить элементы контрольного списка, вы будете использовать Codable
протокол Swift, который позволяет объектам, поддерживающим Codable
протокол, хранить себя в структурированном формате файла (a structured file format).
На самом деле вам не нужно сильно заботиться о формате, используемом Codable
. В данном случае это файл .plist, но вы не собираетесь связываться с этим файлом напрямую. Все, что вас волнует, — это то, что данные хранятся в каком-то файле в папке «Документы» приложения, а технические детали вы оставите Codable
на потом .
Кстати, вы уже использовали двоюродного брата Codable
из Objective-C NSCoder
за кулисами в раскадровках. Когда вы добавляете контроллер представления в раскадровку, Xcode использует NSCoder
систему для записи этого объекта в файловую кодировку. Затем, когда ваше приложение запускается, снова использует NSCoder
для чтения объектов из файла раскадровки – декодирования. Протокол Codable
работает аналогично.
==Процесс преобразования объектов в файлы и обратно== также известен как сериализация (serialization). Это большая тема в разработке программного обеспечения.
Мне нравится думать обо всем этом процессе как о замораживании объектов. Вы берете живой объект и замораживаете его так, чтобы он был подвешен во времени.
Вы сохраняете этот замороженный объект в файл на флэш-накопителе устройства, где он проведет некоторое время в криостазе. Позже вы можете прочитать этот файл в память и разморозить объект, чтобы снова вернуть его к жизни.

Процесс замораживания (сохранения) и размораживания (загрузки) объектов
Сохранение данных в файл (Save data to a file)
- Добавьте следующий метод в ChecklistViewController.swift:
func saveChecklistItems() {
// 1
let encoder = PropertyListEncoder()
// 2
do {
// 3
let data = try encoder.encode(items)
// 4
try data.write(
to: dataFilePath(),
options: Data.WritingOptions.atomic)
// 5
} catch {
// 6
print("Error encoding item array: \(error.localizedDescription)")
}
}
Этот метод берет содержимое items
массива, преобразует его в блок двоичных данных, а затем записывает эти данные в файл. Давайте рассмотрим прокомментированные строки шаг за шагом, чтобы понять код:
- Во-первых, создайте экземпляр
PropertyListEncoder
, который будет кодироватьitems
массив и всеChecklistItem
s в нем в какой-то двоичный формат данных, который может быть записан в файл. - Ключевое слово
do
, с которым вы раньше не сталкивались, создает блок кода для улавливания ошибок Swift. Swift обрабатывает ошибки при определенных условиях, выдавая ошибку. В таких случаях вам нужен блок кода, чтобы поймать ошибку и обработать ее. Ключевое словоdo
указывает на начало такого блока. Вы увидите код перехвата ошибок после комментария №5, где находится ключевое словоcatch
. - Кодер (The encoder), который вы создали ранее, используется для кодирования
items
массива. Методencode
выдает ошибку Swift, если по какой — то причине он не может закодировать данные-например, данные не в ожидаемом формате, или они повреждены и т.д. Ключевое словоtry
указывает, что вызовencode
может завершиться неудачно, и если это произойдет, то он выдаст ошибку. Если у вас нет ключевого словаtry
перед вызовом метода, который выдает ошибку, вы получите ошибку Xcode. Попробуй и увидишь. Если вызовencode
завершится неудачно, выполнение немедленно перейдет кcatch
блоку, а не к следующей строке. - Если константа
data
была успешно создана вызовомencode
в предыдущей строке, то вы записываете данные в файл, используя путь к файлу, возвращаемый вызовомdataFilePath()
. Обратите внимание, что методwrite
также может выдавать ошибку. Итак, опять же, вы должны предшествовать вызову метода другимtry
оператором. - Оператор
catch
указывает блок кода, который будет выполнен, если ошибка была вызвана любой строкой кода в заключающем блокеdo
. - Обработайте пойманную ошибку. Здесь вы просто выводите сообщение об ошибке на консоль Xcode, но можете заметить, что оператор
print
ссылается наerror
переменную. Откуда это взялось? - Когда вы создаете блок кода
do
catch
, вы можете явно проверить наличие определенных типов ошибок. На данный момент мы не будем вдаваться в подробности, но вам нужно знать, что если у вас просто естьcatch
блок, Swift автоматически заполнит локальную переменную с именемerror
, которая будет содержать ошибку, вызванную одним из операторов внутриdo
блока. - Таким образом, вы можете просто ссылаться на эту
error
переменную в любом коде, который вы пишете вcatch
блоке. Это может быть удобно для вывода описательного сообщения об ошибке, которое указывает, что было источником ошибки/сбоя.
Вы заметите, что Xcode показывает ошибку в этот момент, говоря: класс ‘PropertyListEncoder’ требует, чтобы ‘ChecklistItem’ соответствовал ‘Encodable’. (Class ‘PropertyListEncoder’ requires that ‘ChecklistItem’ conform to ‘Encodable’)
Это происходит потому, что любой объект, закодированный (или декодированный) PropertyListEncoder
— или, если уж на то пошло, любым другим кодером/декодером, совместимым с Codable
протоколом, — должен поддерживать Codable
протокол, а ChecklistItem
не поддерживает его.
Кодируемый протокол (The Codable protocol)
Массивы Swift, как и большинство других стандартных объектов и структур Swift, соответствуют Codable
протоколу. Однако в случае массива объекты, содержащиеся в массиве, также должны поддерживаться Codable
, если вы хотите сериализовать массив. Вот почему нам нужно, чтобы класс ChecklistItem
был Codable
совместимый.
Примечание: Иногда при работе с кодом, связанным с поддержкой
Codable
, вы будете видеть сообщения об ошибках или ссылки наEncodable
иDecodable
протоколы. Таким образом, было бы неплохо знать, чтоCodable
на самом деле это протокол, который объединяет эти два других протокола,Encodable
иDecodable
— по одному для каждой стороны процесса сериализации.
Переключитесь на ChecklistItem.swift и измените class
строку следующим образом::
class ChecklistItem: NSObject, Codable {
В приведенном выше примере вы сообщаете компилятору, что ChecklistItem
будет соответствовать Codable
протоколу. Это все, что вам нужно сделать!
- А теперь держись, — слышу я твой голос. “Раньше нам приходилось внедрять методы поддержки протокола. Почему мы не должны делать это здесь?
Помните, как я упоминал ранее, что протоколы могут иметь реализации по умолчанию? Нет? Хорошо, это было в главе Делегаты и протоколы в разделе о протоколах :] Иногда полезно иметь реализацию по умолчанию для протокола, чтобы обеспечить функциональность, которая облегчила бы работу — или охватила бы множество стандартных сценариев.
В нашем случае все свойства ChecklistItem
являются стандартными типами Swift, и Swift уже знает, как кодировать/декодировать эти типы. Таким образом, мы можем просто использовать существующую функциональность без необходимости писать какой-либо собственный код для реализации кодирования/декодирования ChecklistItem
. Удобно, а?
Использование нового метода
Вы должны вызывать новый saveChecklistItems()
метод всякий раз, когда список элементов изменяется.
Упражнение: Где в исходном коде вы бы вызвали этот метод?
Ответ: Посмотрите, где изменяется массив items
. Это происходит внутри ItemDetailViewControllerDelegate
методов. Вот где вечеринка!
- Добавьте вызов
saveChecklistItems()
в конец этих методов в ChecklistViewController.swift:
func itemDetailViewController(
_ controller: ItemDetailViewController,
didFinishAdding item: ChecklistItem
) {
. . .
saveChecklistItems()
}
func itemDetailViewController(
_ controller: ItemDetailViewController,
didFinishEditing item: ChecklistItem
) {
. . .
saveChecklistItems()
}
Давайте не будем забывать о функции swipe-to-delete (для удаления строк свайпом):
override func tableView(
_ tableView: UITableView,
commit editingStyle: UITableViewCellEditingStyle,
forRowAt indexPath: IndexPath
) {
. . .
saveChecklistItems()
}
- И переключение флажка в строке:
override func tableView(
_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath
) {
. . .
saveChecklistItems()
}
Проверьте сохраненный файл
- Запустите приложение сейчас и сделайте что-нибудь, что приведет к сохранению, например, коснитесь строки, чтобы перевернуть галочку, или удалите/добавьте элемент.
- Перейдите в окно Finder, в котором открыт каталог документов приложения.:

Каталог Documents теперь содержит файл Checklists.plist
Теперь в папке Documents есть файл Checklists.plist, содержащий элементы из списка.
Вы можете заглянуть внутрь этого файла, если хотите, но его содержимое не будет иметь большого смысла. Несмотря на то, что это XML, этот файл не предназначался для чтения людьми , а только чем-то вроде PropertyListDecoder
аналога того PropertyListEncoder
, что мы уже использовали.
Если у вас возникли проблемы с просмотром XML, это может быть связано с тем, что файл plist хранится не в виде текста, а в двоичном формате. Некоторые текстовые редакторы поддерживают этот формат файла и могут читать его как текст — BBEdit-хороший вариант, который можно бесплатно скачать в Mac App Store.
Вы также можете использовать функцию быстрого просмотра Finder для просмотра файла. Просто выберите файл в Finder и нажмите пробел.
Естественно, вы также можете открыть файл plist с помощью Xcode.
- Щелкните правой кнопкой мыши файл Checklists.plist и выберите Открыть с помощью ▸ Xcode.

Контрольный список.plist в Xcode
Это все равно не будет иметь особого смысла, но смотреть на это все равно интересно.
Разверните некоторые строки, и вы увидите, что там есть имена ChecklistItem
s, а также их проверенное/непроверенное состояние. Но то, как именно все эти элементы данных сочетаются друг с другом, может пока не иметь для вас особого смысла.

Объекты и документация “NS” (“NS” objects & Documentation)
Объекты, имя которых начинается с префикса “NS”, например NSObject
, NSString
, или NSCoder
, предоставляются платформой Foundation framework. Одна из теорий заключается в том, что NS расшифровывается как NextStep, операционная система 1990-х годов, которая позже стала Mac OS X и которая также легла в основу iOS.
Если вам интересно, как именно работают такие объекты, как NSObbject
и NSString
, вы можете Alt/Option-щелкнуть любой элемент в исходном коде, чтобы открыть всплывающее окно с кратким описанием. И это работает и для объектов с префиксом, отличным от NS :] На самом деле, вы можете найти подробную информацию о любом классе, объекте, переменной или методе таким образом в Xcode.

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

Загрузите файл (Load the file)
Saving — это все хорошо, но само по себе довольно бесполезно. Итак, давайте также реализуем загрузку файла Checklists.plist. Это очень просто – вы собираетесь сделать то же самое, что только что сделали для кодирования массива элементов, но в обратном порядке.
Чтение данных из файла (Read data from a file)
- Переключитесь на ChecklistViewController.swift и добавьте следующий новый метод:
func loadChecklistItems() {
// 1
let path = dataFilePath()
// 2
if let data = try? Data(contentsOf: path) {
// 3
let decoder = PropertyListDecoder()
do {
// 4
items = try decoder.decode(
[ChecklistItem].self,
from: data)
} catch {
print("Error decoding item array: \(error.localizedDescription)")
}
}
}
Давайте пройдем через это шаг за шагом:
- Во-первых, вы помещаете результаты
dataFilePath()
во ==временную константу== с именемpath
. - Попробуйте загрузить содержимое Checklists.plist в новый
Data
объект. Командаtry?
пытается создатьData
объект, но возвращаетсяnil
, если это не удается. Вот почему вы написали об этом вif let
заявлении. Почему он потерпел неудачу? Если нет файла Checklists.plist, то, очевидно, нет никакихChecklistItem
объектов для загрузки. Это то, что происходит, когда приложение запускается в первый раз. В этом случае вы пропустите остальную часть этого метода. - Кроме того, обратите внимание, что это еще один способ использования
try
оператора — вместо того, чтобы заключатьtry
оператор вdo
блок, как вы делали ранее, вы можете иметьtry?
оператор, который указывает, чтоtry
может потерпеть неудачу, и если это произойдет, то он вернетсяnil
. Используете ли выdo
блочный подход или этот, полностью зависит от вас. - Когда приложение найдет файл Checklists.plist, вы загрузите весь массив и его содержимое из файла с помощью a
PropertyListDecoder
. Итак, создайте экземпляр декодера. - Загрузите сохраненные данные
items
обратно с помощью метода декодераdecode
. Единственным элементом, представляющим интерес здесь, будет первый переданный параметрdecode
. Декодер должен знать, какой тип данных будет результатом операции декодирования, и вы сообщаете ему об этом, указывая, что это будет массивChecklistItem
объектов.
Это заполняет массив точными копиями ChecklistItem
объектов, которые были заморожены в файле Checklists.plist.
Теперь у вас есть свой loadChecklistItems()
метод, но он должен быть вызван откуда-то, чтобы это сработало. Есть несколько мест, из которых вы можете это сделать.
Взгляните на текущий код в ChecklistViewController.swift — казалось бы, использование viewDidLoad()
является очевидным выбором, так как именно там мы в настоящее время загружаем статические данные для приложения. Итак, давайте очистим статические элементы данных и просто загрузим сохраненные данные из viewDidLoad
!
Загрузите сохраненные данные при запуске приложения (Load the saved data on app start)
Вот что вам нужно сделать:
- Удалите существующие строки для создания пяти статических
ChecklistItem
экземпляров и операторов печатиviewDidLoad
и замените их вызовомloadChecklistItems
следующим образом:
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.prefersLargeTitles = true
// Load items
loadChecklistItems()
}
Примечание: Если вы решили включить большие заголовки через раскадровку, то у вас не будет строки сразу после вызова
super.viewDidLoad()
.
Вам не нужно добавлять туда комментарии, но всегда хорошо иметь некоторые комментарии в вашем источнике, чтобы вы могли понять свой собственный код через месяц или два (или несколько лет):] Но, как я уже упоминал, если у вас есть хорошие имена методов, комментарии в большинстве случаев действительно не нужны.
Все, что нового в приведенном выше (кроме удаленного кода), — это добавление вызова loadChecklistItems()
, чтобы гарантировать, что сохраненные данные элемента будут загружены обратно при первой загрузке контроллера представления.
- Запустите приложение и внесите некоторые изменения в список дел. Нажмите кнопку Стоп, чтобы завершить работу приложения. Запустите его снова и обратите внимание, что ваши изменения все еще там.
- Снова остановите приложение. Перейдите в окно Finder с папкой Documents и удалите файл Checklists.plist. Запустите приложение еще раз. Теперь у вас должен быть пустой список предметов.
- Добавьте элемент и обратите внимание, что файл Checklists.plist снова появится.
Круто! Вы написали приложение, которое не только позволяет добавлять и редактировать данные, но и сохраняет их между сеансами. Эти методы лежат в основе многих и многих приложений.
Умение использовать навигационный контроллер (a navigation controller), показывать второстепенные экраны и передавать данные через делегатов также является важным навыком разработки iOS.
Инициализаторы
Методы init
являются особыми в Swift. Они используются только тогда, когда вы создаете новые объекты, чтобы сделать эти новые объекты готовыми к использованию.
Думайте об этом как о покупке новой одежды. Одежда находится в вашем распоряжении (память для объекта выделена), но она все еще в сумке. Вам нужно пойти переодеться и надеть новую одежду (инициализация), прежде чем вы будете готовы выйти на вечеринку.
Когда вы пишете следующее, чтобы создать новый объект,
let item = ChecklistItem()
Swift сначала выделяет кусок памяти, достаточно большой, чтобы вместить новый объект, а затем вызывает метод ChecklistItem``init()
s без параметров.
Довольно часто объекты имеют более одного метода инициализации. Какой из них используется, зависит от обстоятельств.
Например, среди init
методов UITableViewController
вы найдете — init(nibName:bundle:)
, init(style:)
и init?(coder:)
. init?(coder:)
используется, когда контроллер представления создается из раскадровки. Но вы также можете создать UITableViewController
экземпляр напрямую, вызвав либо init(nibName:bundle:)
или init(style:)
. Итак, то, как вы инициализируете объект, зависит от обстоятельств.
Реализации этих init
методов, независимо от того, вызываются ли они просто init()``init?(coder:)
или что-то еще, всегда следуют одной и той же серии шагов. Когда вы пишете свои собственные init
методы, вам также нужно придерживаться этих шагов.
Это стандартный способ написания init
метода:
init() {
// Put values into your instance variables and constants. Вводите значения в переменные и константы вашего экземпляра
super.init()
// Other initialization code, such as calling methods, goes here. Другой код инициализации, такой как вызов методов, находится здесь.
}
Обратите внимание, что в отличие от других методов, init
не имеет func
ключевого слова.
Иногда вы увидите, что он написан как override init
или required init?
. Это необходимо, когда вы добавляете init
метод к объекту, который является подклассом какого-либо другого объекта. Об этом позже.
Вопросительный знак указывает на то, когда init?
потенциально может произойти сбой и вернуть nil
значение вместо реального объекта. Вы можете себе представить, что декодирование объекта может завершиться неудачей, если в файле plist недостаточно информации.
Внутри init
метода сначала нужно убедиться, что все переменные и константы вашего экземпляра имеют значение. Напомним, что в Swift все переменные всегда должны иметь значение, за исключением опционалов. Когда вы объявляете переменную экземпляра, вы можете придать ей начальное значение (или инициализировать ее), например:
var checked = false
Также можно написать только имя переменной и ее тип (или объявить переменную), но еще не давать ей значения:
var checked: Bool
В последнем случае вы должны присвоить этой переменной значение в вашем init
методе:
init() {
checked = false
super.init()
}
Вы должны использовать любой из этих подходов; если вы вообще не даете переменной значения, Swift считает это ошибкой. Единственным исключением являются опционалы, они не должны иметь значения (в этом случае они есть nil
).
После того как вы задали все значения переменных экземпляра и констант, вы вызываете super.init()
для инициализации суперкласс объекта. Если вы вообще не занимались объектно-ориентированным программированием, вы можете не знать, что такое суперкласс. Все в порядке, мы полностью проигнорируем эту тему на потом.
Просто помните, что иногда объектам нужно отправлять сообщения на что-то вызываемое super
, и если вы забудете это сделать, то, скорее всего, произойдут плохие вещи. После вызова super.init()
вы можете выполнить дополнительную инициализацию, например вызвать собственные методы объекта. Вам не разрешается делать это перед вызовомsuper.init()
, потому что Swift не гарантирует, что все переменные вашего объекта имеют правильные значения до тех пор.
Вам не всегда нужно предоставлять init
метод. Если вашему init
методу ничего не нужно делать — если нет переменных экземпляра для заполнения, — то вы можете полностью исключить его, и компилятор предоставит его вам. В качестве примера взгляните на ChecklistItem
— у него нет init()
метода, так как все его переменные инициализируются при объявлении.
Правила Swift для инициализаторов могут быть немного сложными, но, к счастью, компилятор напомнит вам, когда вы забудете указать init
метод.
Что дальше?
Checklists в настоящее время находится в хорошем месте — у вас есть большая часть функциональности, и нет никаких ошибок. Это хорошее время, чтобы сделать перерыв, поднять ноги и помечтать обо всех интересных приложениях, которые вы скоро напишете :]
Также разумно вернуться назад и повторить те части, о которых вы все еще немного не знаете. Не спешите проходить эти главы — нет никаких призов за то, что вы финишируете первым. Вместо того чтобы торопиться, потратьте время на то, чтобы по-настоящему понять, что вы делаете.