ПрограммированиеiOS разработчик

Опишите жизненный цикл view controller в UIKit, и как неправильная работа с жизненным циклом может привести к утечкам памяти или багам в UI. Какие тонкости стоит знать?

Проходите собеседования с ИИ помощником Hintsage

Ответ

Жизненный цикл UIViewController включает ключевые методы:

  • init(nibName:bundle:) / init(coder:): инициализация
  • loadView: создание иерархии view
  • viewDidLoad: view загружена (идеальное место для первоначальной настройки)
  • viewWillAppear / viewDidAppear: view показывает/показана
  • viewWillDisappear / viewDidDisappear: view скрывается/скрыта
  • deinit: удаление контроллера

Порядок вызова методов:

  • Сначала создается иерархия view (loadView, затем viewDidLoad)
  • При каждом появлении на экране и скрытии вызываются viewWillAppear/viewWillDisappear, затем viewDidAppear/viewDidDisappear
  • После удаления — deinit

Тонкости:

  • В viewDidLoad не стоит обращаться к размерам view — они могут быть неактуальны
  • Важно освобождать ресурсы (наблюдатели, таймеры) в deinit или viewWillDisappear
  • Добавлять subview — в viewDidLoad, но не в viewWillAppear — иначе возможно дублирование при каждом появлении

Пример:

class MyViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() setupViews() setupBindings() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // Subscribe to notifications } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) // Unsubscribe from notifications } deinit { // Clean up } }

Вопрос с подвохом

Вопрос:

Допускается ли вью-контроллеру обращаться к свойствам view вне жизненного цикла (например, в инициализаторе)?

Ответ: Нет, до момента вызова (или переопределения) loadView все свойства, связанные с view, могут быть неинициализированы. Любой доступ к ним вне loadView/viewDidLoad приведёт к крэшу.

Пример:

// Ошибка: self.view ещё не инициализирована! init() { super.init(nibName: nil, bundle: nil) self.view.backgroundColor = .red //КРАШ }

Примеры реальных ошибок из-за незнания тонкостей темы


История

Проект падал из-за преждевременного обращения к self.view в init — view не была создана, приводило к крэшу.


История

В крупных проектах забывали отписаться от NotificationCenter или delegate в viewWillDisappear и/или deinit, что приводило к утечкам памяти (NotificationCenter ссылался на view controller, он не освобождался и продолжал слушать события).


История

В viewWillAppear каждое появление добавлялись новые сабьюхи к view (например, показывали индикатор загрузки), не проверяя существование. В результате при каждом возврате на экран появлялось всё больше дублирующихся индикаторов, UI выходил из строя.