AI
prepair.app
Пройти интервью →
EnglishУкраїнськаРусский
🍏

Вопросы на собеседовании iOS разработчика

Собеседование iOS-разработчика проверяет систему типов Swift, понимание ARC и умение рассуждать о состоянии интерфейса — плюс обязательно найдётся вопрос, где retain cycle прячется на виду. Ниже — вопросы, которые задают чаще всего, каждый с эталонным ответом.

Junior · без опыта / до 1 годаMiddle · 2–4 года опытаSenior · 5+ лет опыта

Что спрашивают

Swift и опционалы
ARC и управление памятью
SwiftUI и UIKit
Конкурентность: async/await, Combine, GCD
Архитектура и состояние
Хранение данных и сеть

12 реальных вопросов с ответами

К каждому вопросу — эталонный ответ, с которым можно сравнить свой.

1

Какую проблему решают опционалы и как безопасно их разворачивать?

Ответ

Опционал делает «значения может не быть» частью типа, поэтому компилятор заставляет обработать пустой случай, а не падать в рантайме, как это делает нулевой указатель. Безопасное разворачивание — это if let или guard let, когда значение нужно дальше по коду, оператор ?? для значения по умолчанию и опциональная цепочка, если интересен только успешный путь. Force unwrap через ! — это обещание компилятору, что значение есть: допустимо для outlet-ов и ошибок программиста, но никогда для данных из сети.

2

В чём разница между struct и class в Swift? Что берёте по умолчанию?

Ответ

Struct — значимый тип: присваивание копирует его, наследования нет, и по возможности он размещается на стеке. Class — ссылочный тип с идентичностью, наследованием, deinit и накладными расходами ARC. Конвенция Swift — начинать со struct и переходить на class только когда нужна ссылочная семантика: разделяемое изменяемое состояние, совместимость с Objective-C или жизненный цикл, за которым нужно наблюдать.

3

Как работает ARC и из-за чего возникает retain cycle?

Ответ

ARC считает количество сильных ссылок на объект и освобождает его, когда счётчик обнуляется — это вставленные на этапе компиляции вызовы retain/release, а не сборщик мусора в рантайме. Retain cycle возникает, когда два объекта держат сильные ссылки друг на друга и ни один счётчик не доходит до нуля: классика — вью-контроллер владеет замыканием, которое сильно захватывает self. Разрывается через [weak self] или [unowned self] в списке захвата.

4

Когда использовать weak, а когда unowned?

Ответ

Оба не увеличивают счётчик ссылок. weak делает ссылку опциональной и обнуляет её при освобождении объекта, поэтому безопасен, если другой объект может умереть первым — делегаты это стандартный пример. unowned неопционален и предполагает, что объект переживёт вас; если предположение нарушится, приложение упадёт. unowned берут только когда связь жизненных циклов действительно гарантирована, во всех сомнительных случаях — weak.

5

В чём разница между @State, @Binding, @StateObject и @ObservedObject?

Ответ

@State — локальное состояние значимого типа, которым владеет вью; SwiftUI хранит его вне структуры, поэтому оно переживает перерисовки. @Binding — ссылка на чужое состояние с правом записи, именно так дочерний вью меняет значение родителя. @StateObject создаёт и владеет ссылочным observable-объектом и инициализируется ровно один раз. @ObservedObject наблюдает за объектом, которым владеет кто-то другой — если поставить его там, где нужен @StateObject, объект будет пересоздаваться на каждой перерисовке и молча терять состояние.

6

Как SwiftUI решает, что вью нужно перерисовать?

Ответ

Вью — это лёгкая структура, описывающая интерфейс; SwiftUI пересобирает её body всякий раз, когда меняется прочитанная ею зависимость, затем сравнивает результат с предыдущим деревом и применяет только различия. Зависимости — это те property wrapper-ы, которые вью реально читает, плюс Environment. Проблемы производительности почти всегда идут от состояния, поднятого слишком высоко по дереву: не связанное изменение инвалидирует большое поддерево.

7

Что меняет async/await по сравнению с completion-хендлерами и GCD?

Ответ

async/await позволяет читать асинхронный код сверху вниз, поэтому обработка ошибок идёт обычным try/catch, нет «пирамиды вложенности» и забытых веток завершения. Компилятор следит, чтобы await вызывался в async-контексте, а структурная конкурентность привязывает жизнь дочерних задач к родительской, так что отмена распространяется автоматически. GCD остаётся актуальным для легаси и тонкого управления очередями, но новый код стоит писать на async/await, оставив Combine для настоящих потоков значений во времени.

8

Что такое escaping-замыкание и зачем нужно это ключевое слово?

Ответ

Замыкание считается escaping, если оно сохраняется и вызывается уже после возврата из функции — например, completion-хендлер сетевого запроса. Swift требует пометки @escaping, потому что не-escaping замыкание можно агрессивнее оптимизировать и оно не может случайно сильно захватить self. Эта пометка ещё и сигнал подумать о семантике захвата: именно в escaping-замыканиях рождаются retain cycle.

9

Разберите жизненный цикл UIViewController. Где размещать код вёрстки?

Ответ

viewDidLoad выполняется один раз после загрузки иерархии вью и предназначен для разовой настройки. viewWillAppear и viewDidAppear вызываются при каждом показе — туда идёт то, что должно повторяться: обновление данных, запуск анимации. Вёрстка, зависящая от финальных размеров, живёт в viewDidLayoutSubviews, потому что в viewDidLoad границы ещё не окончательные — вычисление фреймов там очень частый баг, который проявляется только на другом размере экрана.

10

Как выбрать между Core Data, SwiftData и просто файлами?

Ответ

Core Data оправдывает свою сложность, когда данные реляционные и нужны запросы, ленивая подгрузка и отслеживание изменений на большом графе объектов. SwiftData — современная нативная для Swift обёртка над тем же стеком, дефолт для новых приложений на свежих версиях ОС. Для десятка настроек берут UserDefaults, а для кеша декодированных моделей обычные Codable-файлы или лёгкое key-value хранилище понятнее, чем полноценный persistent container.

11

Как диагностировать медленный запуск или утечку памяти?

Ответ

Для времени запуска берут шаблон App Launch в Instruments и смотрят, что выполняется до первого кадра; обычные виновники — синхронная работа с диском или сетью в didFinishLaunching и тяжёлый граф зависимостей, собираемый заранее. Для утечек Memory Graph Debugger показывает объекты, которые должны были освободиться, и того, кто на них ещё ссылается, — это напрямую вскрывает retain cycle. Инструмент Leaks ловит классические утечки, но граф удобнее для циклов, потому что он называет удерживающую ссылку.

12

Что вы тестируете в iOS, а что сознательно не тестируете?

Ответ

Юнит-тестами покрывают бизнес-логику, вью-модели и чистые преобразования — всё, у чего есть вход и выход и нет зависимости от UIKit. XCUITest берут на небольшое число критичных сквозных сценариев вроде входа и покупки, потому что UI-тесты медленные и хрупкие в больших количествах. Обычно не окупается тестирование поведения фреймворков Apple и попиксельной вёрстки; снапшот-тесты тут помогают, но ломаются на каждой смене версии ОС, поэтому их держат мало и осознанно.

🦎

Прочитать ответы недостаточно

На собеседовании ты говоришь вслух под давлением. Кем задаст эти же вопросы, оценит каждый ответ и покажет, что именно подтянуть.

Пройти интервью iOS (Swift) →
Бесплатно · 3 интервью в месяц

Другие специализации

🔍QA Manual🤖QA AutomationJava Backend🐍Python Backend🐘PHP Backend🦫Go Backend🟢Node.js Backend⚛️React Frontend💚Vue Frontend🅰️Angular FrontendNext.js🤖Android (Kotlin)⚙️DevOps / SRE🗄️Data Engineer📈Business Analyst🎯Product Manager📋Project Manager🎨UI/UX Designer📣Marketing