Співбесіда iOS-розробника перевіряє систему типів Swift, розуміння ARC і вміння міркувати про стан інтерфейсу — плюс обов'язково трапиться питання, де retain cycle ховається на видноті. Нижче — питання, які ставлять найчастіше, кожне з еталонною відповіддю. Senior: архітектуру, трейд-офи, менторство та прийняття рішень.
1
Коли використовувати weak, а коли unowned?
Відповідь
Обидва не збільшують лічильник посилань. weak робить посилання опціональним і обнуляє його при звільненні обʼєкта, тому він безпечний, якщо інший обʼєкт може померти першим — делегати це стандартний приклад. unowned неопціональний і припускає, що обʼєкт переживе вас; якщо припущення порушиться, застосунок впаде. unowned беруть лише коли звʼязок життєвих циклів справді гарантований, у всіх сумнівних випадках — weak.
2
У чому різниця між @State, @Binding, @StateObject та @ObservedObject?
Відповідь
@State — локальний стан значимого типу, яким володіє вʼю; SwiftUI зберігає його поза структурою, тому він переживає перемальовування. @Binding — посилання на чужий стан із правом запису, саме так дочірнє вʼю змінює значення батьківського. @StateObject створює і володіє посилальним observable-обʼєктом та ініціалізується рівно один раз. @ObservedObject спостерігає за обʼєктом, яким володіє хтось інший — якщо поставити його там, де потрібен @StateObject, обʼєкт перестворюватиметься на кожному перемальовуванні й мовчки втрачатиме стан.
3
Як SwiftUI вирішує, що вʼю треба перемалювати?
Відповідь
Вʼю — це легка структура, що описує інтерфейс; SwiftUI перезбирає її body щоразу, коли змінюється прочитана нею залежність, потім порівнює результат із попереднім деревом і застосовує лише відмінності. Залежності — це ті property wrapper-и, які вʼю справді читає, плюс Environment. Проблеми продуктивності майже завжди йдуть від стану, піднятого зависоко по дереву: не повʼязана зміна інвалідує велике піддерево.
4
Що змінює async/await порівняно з completion-хендлерами та GCD?
Відповідь
async/await дозволяє читати асинхронний код зверху вниз, тому обробка помилок іде звичайним try/catch, немає «піраміди вкладеності» й забутих гілок завершення. Компілятор стежить, щоб await викликався в async-контексті, а структурна конкурентність привʼязує життя дочірніх задач до батьківської, тож скасування поширюється автоматично. GCD лишається актуальним для легасі й тонкого керування чергами, але новий код варто писати на async/await, залишивши Combine для справжніх потоків значень у часі.
5
Що таке escaping-замикання і навіщо це ключове слово?
Відповідь
Замикання вважається escaping, якщо воно зберігається і викликається вже після повернення з функції — наприклад, completion-хендлер мережевого запиту. Swift вимагає позначки @escaping, бо не-escaping замикання можна агресивніше оптимізувати і воно не може випадково сильно захопити self. Ця позначка ще й сигнал подумати про семантику захоплення: саме в escaping-замиканнях народжуються retain cycle.
6
Розберіть життєвий цикл UIViewController. Де розміщувати код верстки?
Відповідь
viewDidLoad виконується один раз після завантаження ієрархії вʼю і призначений для разового налаштування. viewWillAppear і viewDidAppear викликаються при кожному показі — туди йде те, що має повторюватися: оновлення даних, запуск анімації. Верстка, що залежить від фінальних розмірів, живе у viewDidLayoutSubviews, бо у viewDidLoad межі ще не остаточні — обчислення фреймів там дуже часта помилка, яка проявляється лише на іншому розмірі екрана.
7
Як обрати між Core Data, SwiftData і просто файлами?
Відповідь
Core Data виправдовує свою складність, коли дані реляційні та потрібні запити, лінива підвантаження і відстеження змін на великому графі обʼєктів. SwiftData — сучасна нативна для Swift обгортка над тим самим стеком, дефолт для нових застосунків на свіжих версіях ОС. Для десятка налаштувань беруть UserDefaults, а для кешу декодованих моделей звичайні Codable-файли або легке key-value сховище зрозуміліші, ніж повноцінний persistent container.
8
Як діагностувати повільний запуск або витік памʼяті?
Відповідь
Для часу запуску беруть шаблон App Launch в Instruments і дивляться, що виконується до першого кадру; звичайні винуватці — синхронна робота з диском чи мережею в didFinishLaunching і важкий граф залежностей, що збирається заздалегідь. Для витоків Memory Graph Debugger показує обʼєкти, які мали звільнитися, і того, хто на них ще посилається, — це напряму розкриває retain cycle. Інструмент Leaks ловить класичні витоки, але граф зручніший для циклів, бо він називає утримуюче посилання.
9
Що ви тестуєте в iOS, а що свідомо не тестуєте?
Відповідь
Юніт-тестами покривають бізнес-логіку, вʼю-моделі та чисті перетворення — усе, що має вхід і вихід і не залежить від UIKit. XCUITest беруть на невелику кількість критичних наскрізних сценаріїв на кшталт входу і покупки, бо UI-тести повільні та крихкі у великих обсягах. Зазвичай не окупається тестування поведінки фреймворків Apple і попіксельної верстки; снапшот-тести тут допомагають, але ламаються на кожній зміні версії ОС, тому їх тримають мало і свідомо.
🦎
Прочитати відповіді недостатньо
На співбесіді ти говориш вголос під тиском. Кем поставить ті самі питання, оцінить кожну відповідь і покаже, що саме підтягнути.
Пройти інтерв'ю Senior iOS (Swift) →Безкоштовно · 3 інтерв'ю на місяць