Что такое UIViewController и за что он отвечает в iOS?
Что проверяют
Знает ли кандидат жизненный цикл экрана наизусть и понимает ли границы ответственности контроллера. Вопрос базовый, поэтому ценится точность: какой метод вызывается один раз, какой при каждом показе, и что в каком из них допустимо делать.
Как отвечать
UIViewController владеет одним экраном: создаёт корневое представление, связывает его с данными, реагирует на появление и уход, участвует в навигации и в цепочке респондеров.
Жизненный цикл разложить по шагам. Представление создаётся лениво, при первом обращении к нему. Метод loadView собирает корневое представление, если оно не приходит из nib. viewDidLoad вызывается один раз после загрузки. viewWillAppear и viewDidAppear вызываются при каждом показе, парные им методы при уходе. Методы вокруг раскладки подвьюшек вызываются на каждом проходе layout, и только после них известны настоящие размеры.
Отсюда раскладка обязанностей: разовая настройка в viewDidLoad, подписки и обновление данных при появлении с отпиской при уходе, всё, что зависит от размеров, после раскладки.
Композицию тоже ждут: контейнерные контроллеры (навигационный, вкладки, свой собственный) подключают дочерний через addChild и didMove(toParent:), модальные экраны показывают через present, а данные передаёт вызывающая сторона или координатор. И граница: контроллер это связующее звено, а сетевые запросы и бизнес-правила выносятся во вью-модель, интерактор или сервис.
Где спотыкаются
Грузят данные только в viewDidLoad и удивляются, что при возврате с соседнего экрана на экране старое состояние. Обратная крайность тоже частая: подписку оформляют при каждом появлении и ни разу не снимают, обработчик срабатывает по нескольку раз.
Читают размеры до раскладки. В viewDidLoad границы представления ещё не те, что на экране, и расчёты по ним дают смещённую вёрстку на части устройств.
Ждут, что контроллер сам освободит память при предупреждении системы. Автоматическая выгрузка представлений давно убрана, а реальные утечки приходят из сильных ссылок в замыканиях, таймерах и делегатах: deinit не вызывается, экран закрыт, но продолжает жить и обрабатывать события.