В каком методе жизненного цикла UIViewController лучше всего сохранять состояние или контекст?

Ответ

Для сохранения контекста, связанного с UI или навигацией, используйте viewWillDisappear(_:). Для сохранения данных модели или состояния приложения — applicationWillResignActive в AppDelegate/SceneDelegate.

Почему viewWillDisappear(_:)? Это последний гарантированный момент в жизненном цикле контроллера, когда он ещё находится в иерархии и его view видимы. Метод deinit не подходит для UI-операций, так как view к этому моменту уже удалены.

Пример сохранения навигационного состояния:

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    // Проверяем, что контроллер действительно скрывается (а не показывается другой поверх)
    if self.isMovingFromParent || self.isBeingDismissed {
        // Сохраняем контекст
        UserDefaults.standard.set(selectedItemId, forKey: "lastSelectedItem")
        // Или передаем данные обратно через делегат/closure
        delegate?.viewControllerWillClose(with: data)
    }
}

Когда использовать deinit? Только для очистки ресурсов самого контроллера (например, отписки от уведомлений, которые были добавлены в viewDidLoad). Не полагайтесь на него для сохранения данных, так как его вызов зависит от ARC и может быть отложен.

Лучшая практика: Разделяйте сохранение UI-состояния (в viewWillDisappear) и сохранение данных модели (в ответ на события жизненного цикла приложения).

Ответ 18+ 🔞

А, ну вот, опять этот вечный спор, где что сохранять, а? Сидишь такой, пишешь приложение, и тут бац — пользователь свернул его, а у тебя всё поехало, данные потерялись, и ты сидишь и думаешь: «Ну что за пиздец, я же всё сохранил, вроде?». А оказывается, ты сохранял не там, где надо, чувак!

Смотри, чтобы не было вот этого вот «ой, всё», запомни раз и навсегда, как таблицу умножения. Есть два священных места, куда ты должен класть свои сохранёнки, и они — не соседние полки в холодильнике.

Первое — это для всякой UI-хероты, типа какой пункт в списке выбран или на какую кнопку тыкали. Для этого у нас есть старый добрый viewWillDisappear(_:). Почему он? Да потому что это последний момент, когда твой контроллер ещё жив-здоров, вьюхи его на месте, и можно спокойно сказать: «Так, братва, запоминаем, что мы тут натворили».

Но, внимание, ебушки-воробушки! Не надо сохранять всё подряд каждый раз, когда показывается поп-овер или что-то поверх. Только когда контроллер реально уходит в небытие — его выпихивают из стека навигации или закрывают. Для этого есть проверочки.

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    // Смотри, не обосрись тут! Сохраняем только если нас реально выгоняют.
    if self.isMovingFromParent || self.isBeingDismissed {
        // Вот тут уже можно смело писать в UserDefaults или куда ты там привык
        UserDefaults.standard.set(selectedItemId, forKey: "lastSelectedItem")
        // Или пинать делегата: «На, мудак, забери данные обратно!»
        delegate?.viewControllerWillClose(with: data)
    }
}

Второе место — это для серьёзных дел, для данных модели, которые должны пережить даже если приложение накрылось медным тазом. Вот это уже не к контроллеру, а к AppDelegate или SceneDelegate. Там ловишь событие applicationWillResignActive. Пользователь нажал Home, получил звонок, или уведомление с котиком перекрыло экран — вот тут-то и надо срочно всё писать в базу, в файл, куда угодно, лишь бы не потерять. Потому что дальше приложение могут и прибить, а deinit твоего контроллера может и не дождаться, он же от ARC зависит, эта хитрая жопа.

А про deinit что? Его, блядь, только для уборки за собой используй! Отписался от нотификаций, которые в viewDidLoad навешал, почистил какие-то свои референсы — и всё. Полагаться на него для сохранения — это как надеяться, что ты вспомнишь пароль от вайфая, когда уже будешь падать с десятого этажа. Бесполезно и поздно.

Короче, золотое правило, чтобы не было волнения ебать:
UI-состояние (что на экране) — сохраняешь в viewWillDisappear(_:).
Данные приложения (что в голове) — сохраняешь в applicationWillResignActive.
А в deinit только мусор за собой выносишь. Всё, вопрос закрыт, можно идти пить чай.