Когда и как выделяется память для root view у UIViewController?

Ответ

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

Последовательность инициализации:

  1. Инициализация контроллера: init(nibName:bundle:) или init(coder:). Представление еще не создано.
  2. Первый доступ к view: Система проверяет, загружено ли представление. Если нет — вызывает метод loadView().
  3. Метод loadView():
    • Если вы переопределили его и создали представление вручную, оно присваивается свойству view.
    • Если нет, система пытается загрузить его из storyboard или xib-файла.
    • Если и это не удалось, создается пустой объект UIView.
  4. Вызов viewDidLoad(): Сразу после загрузки представления и выделения памяти. Здесь можно выполнять дополнительную настройку.

Пример, демонстрирующий ленивую загрузку:

class MyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        print("View loaded and memory allocated.")
    }
}

let vc = MyViewController()
print("Controller created. View is nil? (vc.viewIfLoaded == nil)") // true

// Память выделяется здесь:
_ = vc.view // Триггерит loadView() -> viewDidLoad()
print("View accessed. View is nil? (vc.viewIfLoaded == nil)") // false

Важно: Не обращайтесь к view контроллера до его загрузки (например, в init), это приведет к преждевременной загрузке.

Ответ 18+ 🔞

А, ну вот, смотри, классика жанра, блядь! Эта самая память для корневого представления — она, сука, ленивая, как мой кот в воскресенье. Выделяется только тогда, когда ты первый раз потыкаешь в это свойство view, но до того, как оно на экран полезет.

Как это всё, блядь, происходит, по шагам:

  1. Родился контроллер. Вызвали init(nibName:bundle:) или init(coder:). Представление? Не, не слышал. Его ещё в проекте нет, блядь.
  2. Первый раз спросили про view. Система такая: «А представление-то загружено?» Видит, что нет, и орет: «loadView(), выходи, падла!»
  3. Метод loadView() выходит, почесывая жопу:
    • Если ты, такой умный, его переопределил и сам собрал вьюху из палок — она и присвоится.
    • Если нет — система начинает шарить, нет ли сториборда или xib'а, откуда можно стырить интерфейс.
    • Если и тут облом — создается просто пустой UIView, типа «на, ебись с ним теперь».
  4. И тут бац — viewDidLoad()! Прямо сразу после того, как вьюха загрузилась и память под неё отгрызли. Вот тут уже можно свою хуйню развешивать.

Смотри, какой прикол, на примере:

class MyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        print("Ну всё, вьюха загрузилась, память съедена.")
    }
}

let vc = MyViewController()
print("Контроллер создан. Вьюха есть? (vc.viewIfLoaded == nil)") // true, нихуя!

// А память-то выделится вот тут, когда мы к ней прикоснёмся!
_ = vc.view // Это триггерит всю цепочку: loadView() -> viewDidLoad()
print("К вьюхе обратились. Теперь есть? (vc.viewIfLoaded == nil)") // false, охуеть!

И главное, запомни, дурак: Не лезь ты к view из инициализатора контроллера! А то получишь преждевременную... эээ... загрузку, блядь. Это никому не нужно. Дождись своего часа.