← Назад к вопросам

Когда создается instance контроллера?

1.0 Junior🔥 252 комментариев
#UIKit и верстка#Архитектура и паттерны

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Когда создается instance UIViewController?

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

Основные сценарии инстанцирования

1. Инициализация через Storyboard (Storyboard/XIB)

Наиболее распространенный случай в проектах, использующих Interface Builder.

  • Процесс: Система создает экземпляр, когда требуется загрузить сцену (scene) из storyboard или отдельный XIB-файл.
  • Триггеры:
    *   **Первоначальная загрузка приложения:** Если в `Main.storyboard` или в `Info.plist` (ключ `UIMainStoryboardFile`) указан **initial view controller**, система создает его экземпляр сразу после запуска приложения.
    *   **Переходы (Segues):** При выполнении segue (show, present modally) система находит соответствующий контроллер в storyboard и создает его экземпляр **перед** выполнением перехода.
    *   **Прямая загрузка из кода:** Использование метода `UIStoryboard(name:bundle:).instantiateViewController(withIdentifier:)` или `UINib(nibName:bundle:).instantiate(withOwner:options:)`.

// Пример: явное создание из Storyboard
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let detailVC = storyboard.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController
// Экземпляр detailVC создан в этой строке

2. Инициализация из кода (Programmatic Initialization)

Прямое создание контроллера с помощью инициализатора. Характерно для проектов без storyboard или для динамической логики.

  • Процесс: Вызывается инициализатор контроллера (init(), init(nibName:bundle:), кастомный init).
  • Триггер: Любое прямое обращение в коде.
// Пример: создание контроллера программно
class ViewController: UIViewController {
    func openSettings() {
        // Экземпляр SettingsViewController создается при вызове инициализатора
        let settingsVC = SettingsViewController(style: .grouped)
        self.present(settingsVC, animated: true)
    }
}

class SettingsViewController: UITableViewController {
    // Кастомный инициализатор
    init(style: UITableView.Style) {
        super.init(style: style)
        // Дополнительная настройка
    }
    required init?(coder: NSCoder) { fatalError() }
}

3. Восстановление состояния (State Restoration)

Если включена state restoration, система может воссоздать экземпляры контроллеров при последующем запуске приложения, чтобы восстановить прежний интерфейс.

  • Триггер: Запуск приложения, если оно было ранее приостановлено/завершено системой.
  • Важно: Контроллер должен соответствовать протоколу UIViewControllerRestoration или иметь назначенный restoration identifier.

4. Работа с UINavigationController и UITabBarController

Контейнерные контроллеры управляют созданием дочерних (child) контроллеров:

  • UINavigationController: Устанавливает корневой контроллер (rootViewController) при инициализации. Последующие контроллеры создаются при вызовах pushViewController(_:animated:) непосредственно перед добавлением в стек.
  • UITabBarController: Массив его viewControllers, как правило, создается при его настройке (часто в viewDidLoad). Все экземпляры могут быть созданы сразу для сохранения состояния вкладок.

Жизненный цикл и порядок вызовов

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

Типичный порядок при загрузке из Storyboard:

  1. init(coder:) — создание экземпляра (декодирование из архива).
  2. awakeFromNib() — отправляется после того, как все объекты из архива загружены и подключены (@IBOutlet).
  3. loadView() / viewDidLoad() — вызываются позже, когда системе или коду потребуется view hierarchy. Это не является частью создания экземпляра.

Рекомендации и лучшие практики

  • Минимальная логика в инициализаторах: Инициализаторы (init) должны быть легкими. Задавайте значения свойствам, но избегайте:
    *   Доступа к `view` контроллера (она еще не загружена).
    *   Сетевых вызовов или тяжелых вычислений.
    *   Обращений к другим, возможно, еще не созданным объектам.
  • Разделение ответственности: Настройку зависимостей (Dependency Injection) лучше выполнять в инициализаторе, а окончательную настройку UI и данных — в viewDidLoad().
  • Ленивая загрузка view: Помните, что создание контроллера и создание его view — это разные этапы. View создается "по требованию", что позволяет оптимизировать память.
// Пример: DI в инициализаторе, настройка UI в viewDidLoad
class ProfileViewController: UIViewController {
    let userService: UserService
    private var user: User?

    // Dependency Injection через инициализатор
    init(userService: UserService) {
        self.userService = userService
        super.init(nibName: nil, bundle: nil)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI() // Настройка элементов интерфейса
        loadData() // Загрузка данных, требующих view
    }

    private func loadData() {
        userService.fetchUser { [weak self] user in
            self?.user = user
            self?.updateUI()
        }
    }
}

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