Що відповідати, щоб пройти співбесіду на Senior iOS Developer в Instagram, Viber або Дію

Вітаю, колеги! Мене звати Дмитро. З 2012 року я професійно займаюся iOS-розробкою. У 2014-2017 роках вів iOS-дайджест на DOU. З 2022 року розвиваю власні проєкти, активно пишу статті на DOU та періодично проходжу співбесіди з iOS.

Професійний розвиток в iOS-розробці є важливою частиною мого життя, і якщо для вас це теж так, впевнений, вам стане в пригоді цей блог. На його написання мене надихнули декілька цікавих співбесід із запитаннями, які відображають поточні реалії iOS-розробки.


Співбесіди еволюціонують з роками. У досвідчених спеціалістів все менше шансів, умовно кажучи, виїхати за рахунок знання ручного управління пам’яттю. Я хотів би поділитися найцікавішими питаннями, які ставили мені на технічних співбесідах з iOS в Instagram, Viber, Tango Me, Дії, Intellias та Playwing, а також навести відповіді.

Усі запитання є практично-орієнтованими, розкривають розуміння критичних нюансів iOS-розробки та дають уявлення про здатність спеціаліста якісно виконувати профільні завдання, використовуючи сучасні технології. Запрошую ділитися в коментарях, які цікаві та важливі питання ставили вам або ставите ви. Також відкритий до дискусії щодо запропонованих відповідей.

Які типи диспетчеризації методів наявні у Swift?

#Swift

Їх чотири:

Inline-диспетчеризація, коли виклик методу замінюється на тіло методу на етапі компіляції. Вона працює найшвидше, але недоліком є збільшення розміру бінарника та часу компіляції.

Статична диспетчеризація, коли на етапі компіляції визначається, яка з імплементацій методу буде викликана. Вона досягається використанням ключових слів static для методів та final для класів, що вказує на неможливість перевизначення. Статична диспетчеризація також використовується для value types, адже в цьому випадку наслідування неможливе. Та для протоколів з асоційованими типами, тому що конкретний тип має бути відомим вже на етапі компіляції. Недоліком статичної диспетчеризації є низька гнучкість та неможливість поліморфізму.

Динамічна диспетчеризація, коли метод вибирається в рантаймі. Це можливо для звичайних класів, які є дуже гнучкими, але, на жаль, за це доводиться платити швидкодією. Під капотом динамічної диспетчеризації знаходяться так звані V Tables (віртуальні таблиці), які містять вказівники на імплементації методів.

Message-диспетчеризація, подібна до Objective-C. У цьому випадку метод буде обраний в рантаймі, базуючись на його назві та аргументах. Цей тип диспетчеризації є найповільнішим, але його перевага в надзвичайній гнучкості. Коли можна модифікувати поведінку в рантаймі, наприклад, використовуючи Method Swizzling, динамічну підміну методу. Це пояснює, чому код на Swift зазвичай працює швидше, ніж на Objective-C: цей ефект дає перевага в швидкодії трьох попередніх типів перед message-диспетчеризацією. Почитати про типи диспетчеризації у Swift можна тут.

У чому різниця між weak та unowned?

#Swift

На відміну від strong, обидва ключових слова означають, що retain count не буде збільшуватись. При деалокації об’єкта weak-посилання стають nil. Ключове слово unowned розраховане на ситуації, коли ми не використовуємо Optional, відповідно посилання не може прийняти значення nil. Детально це питання розглянуто тут.

Що відбудеться, якщо виконати наведений нижче код?

#Multithreading

serialQueue.sync {
    print("1")
}

serialQueue.async {
    print("2")
}

serialQueue.sync {
    print("3")
}

Якщо черга, на якій все це виконується, і є та сама serialQueue, то буде дедлок. Перша sync-операція почне виконуватись негайно та заблокує потік. Друга sync-операція чекатиме на завершення першої, а перша чекатиме на звільнення потоку.

Щоб розірвати дедлок, потрібно або замінити sync на async, або викликати операції на окремому потоці. Тобто ось так:

serialQueueA.sync {
    serialQueueB.sync {
        print("1")
    }

    serialQueueB.async {
        print("2")
    }

    serialQueueB.sync {
        print("3")
    }
}

У цьому випадку в консоль буде надруковано:
1
2
3
Порядок буде саме таким, адже операції виконуються на послідовній черзі саме в тому порядку, в якому їх поставили на виконання.

Чому SwiftUI використовує структури для представлень?

#SwiftUI

Структури є простішими за класи, але швидкодія далеко не єдина причина вибору структур для SwiftUI Views. Ці структури постійно копіюються при перемальовці SwiftUI, адже вони є value type. Але потужність сучасних iPhone дозволяє про це не хвилюватись. Справжня причина такого дизайн-рішення полягає в простоті структур, які не наслідують сотні properties, що мають UIViews, і не мають ризиків їхньої безконтрольної зміни. Почитати про це детальніше можна тут.

GeometryReader — що це і для чого він потрібен?

#SwiftUI

Коротко: Geometry Reader дозволяє вкладеним представленням дізнатися розмір і розташування представлень, в які вони вкладені. Тут Hacking with Swift радить зазначити, що структурою GeometryReader часто зловживають. Справа в тому, що в SwiftUI лейаут менеджиться системою, і в більшості випадків вкладеним представленням немає потреби знати про геометрію представлень, в які вони вкладені. Почитати про GeometryReader можна тут.

Як анімовано змінити положення UIView?

#UIKit

Змінити константу констрейнту — перша відповідь, що приходить в голову. Це можна зробити через метод UIView animate або UIViewPropertyAnimator. Але важливо не забути, що після цього потрібно викликати setNeedsLayout та layoutIfNeeded для оновлення лейауту, без цього оновлення констрейнтів не вступить в силу. Це запитання ставили на StackOverflow.

Що таке intrinsic content size?

#UIKit

Intrinsic content size — це перемінна (var), яка повідомляє, яким є бажаний розмір UIView, щоб її зміст був відображений найкращим чином. Ця перемінна може бути перевизначена за допомогою override, що дає змогу керувати бажаним розміром UIView. Auto Layout спирається на Intrinsic Content Size, визначаючи, якими будуть параметри розміру UIView, якщо вони не задані констрейнтами. Intrinsic content size базується на змісті UIView і не гарантує, що в підсумку розмір буде саме таким. При визначенні кінцевого розміру будуть братися до уваги Content Hugging Priority та Compression Resistance Priority. Почитати про це можна тут.

Як визначити Dark Mode?

#iOS

В UIKit — за допомогою UITraitCollection, property UIViewController. В SwiftUI — за допомогою ColorScheme. Приклади дивіться тут.

Які патерни проєктування ви знаєте?

#ComputerScience

Безумовно, вам доводилося використовувати в роботі багато патернів проєктування, але варто освіжити в пам’яті назви та дизайн основних із них. Це можна зробити, подивившись набір з трьох відео на Kodeco.

Які переваги та недоліки має VIPER в порівнянні з MVVM?

#Architecture

Перевагою VIPER є виділення шару Router, відповідального за навігацію, з шару View. Але насамперед виділення шару Interactor, відповідального за логіку доступу до даних, від шару Entity, що забезпечує простоту даних. Саме розділення Model на Interactor та Entity є ключовою перевагою VIPER перед MVVM, адже забезпечує простоту класів та структур та їхню відповідність принципу Single Responsibility. Дивіться порівняння VIPER та MVVM тут.

Чим VIPER відрізняється від VIP?

#Architecture

VIP є модифікацією VIPER. Відмінність в тому, як рухаються дані. Якщо в центрі VIPER знаходиться Presenter, який виконує функцію посередника між View та Interactor, то в VIP дані йдуть по колу: з Presenter вони потрапляють у View, з View в Interactor, а з Interactor в Presenter. VIP краще узгоджується з принципом Single Responsibility, але VIPER дозволяє тримати логіку в єдиному місці, яким є Presenter. Почитати про VIP можна тут.

Що ви знаєте про TCA?

#Architecture

TCA є бібліотекою для побудови архітектури, адаптованої під SwiftUI. Одним з попередників TCA є запропонована Facebook архітектура Redux, яка спирається на єдине джерело даних. Архітектура TCA складається з функцій (Features), які містять State (стан, рівень даних), Action (дії, які можуть відбутися в цій функції), Reducer (описує, як має змінитися стан, базуючись на певній дії), Store (двигун функції, саме його буде зберігати SwiftUI View). Мабуть, найкраще TCA описує офіційна документація. Також в ній є посилання на інші, менш відомі архітектурні бібліотеки, з якими як мінімум варто ознайомитись.

Як визначити та розірвати Retain Cycle?

#Debugging

Для пошуку Retain Cycle треба використовувати Memory Graph Debugger, але Instruments також може стати в пригоді, показуючи витоки пам’яті. Знайшовши їх, ми розриваємо зв’язок strong-strong, роблячи зв’язок з одного боку weak.

Як визначити розмір картинок до того, як вони завантажилися з сервера, щоб плейсхолдери відразу мали потрібний розмір?

#SystemDesign

Попросити бекенд-розробників, щоб вони передавали розмір картинки в одному JSON з її URL.

Поясніть нотацію Big Theta. Як вона використовується для оцінки складності алгоритму?

#Algorithms

Якщо нотація Big O вказує на максимальний обсяг ресурсів часу та пам’яті, який може знадобитися для виконання алгоритму, то Big Omega — на мінімальний. Графік Big Theta описує точну поведінку алгоритму між Big O та Big Omega. У подробицях про Big Omega можна почитати тут, про Big Theta — тут.

Підсумки

Для ґрунтовної та системної підготовки я би радив ознайомитись зі списком, що містить 150 запитань з iOS-розробки від Hacking with Swift. Звичайно, сліпе зазубрювання відповідей буде лише марною тратою часу. Ніщо не замінить практичного досвіду. Незалежно від того, чи є у вас працедавець в цей момент, важливо програмувати щодня. Ба більше, я вважаю, що робочі навички є цінними незалежно від того, здобуті вони на оплачуваній роботі чи на власному проєкті. За умови, що ви не халтурите. Нерідко власні проєкти є єдиною можливістю опанувати певну технологію. У мене так було зі SwiftUI, Combine та SwiftData. Але все ж обговорення конкретних запитань та тем зі співбесід є корисним, тому що це формує уявлення, на що звернути увагу. Сподіваюся, цей блог підсвітить, в яких напрямках вам слід працювати над своїми знаннями та навичками, щоб перейти на наступну сходинку в кар’єрі.

👍ПодобаєтьсяСподобалось3
До обраногоВ обраному4
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

я б задавав би питання на роздуми, ідеї та можливості
— є у нас таке то середовище, щоб ви зробили?
— є така то кодова база, воно старе як *авно мамонта, і треба переписати на swift + swiftui/uikit, як підішли б до вирішення питання, щоб вибрали, чим би керувалися?
— розглянути разом завдання з реальним кодом, обговорити можливе покращення

тобто якщо підбити підсумок, я б через такі питання попробував би побачити як розробник думає, що бере до уваги, яка його позиція. Senior більше має вміти прийняти правильне рішення на основі досвіду, попередньо аналізуючи проблеми та враховуючи prons/cons того чи іншого інструменту, підходу, а не бігти одразу і пиляти щось.

Тому concurrency має місце чи якісь архітектури, але якщо толковий dev то TCA та все інше що може бути новеньке, але до чого руки не доходили дуже легко вивчається у середовищі де це необхідно.

Але я проти коду який ніколи в розробці не використовується, типу такого:
DQ.main.async {
print(2)
DQ.main.sync {
print(3)
}
}
print(1)

або який порядок ініціалізації і наводимо клас, статік мембери в класі, lazy, init, etc)
за весь період роботи ніколи таке не знадобилося)

Але я проти коду який ніколи в розробці не використовується, типу такого

Мав недавно півторагодинну співбесіду, яка складалася виключно з таких сніппетів-пазлерів, які, на думку інтервʼюера, дуже добре перевіряють глибину мого розуміння речей. Співбесіда була на техліда команди 🤷‍♂️

жаль, що такий досвід, можливо варто було б окремо проводити курси для навчання проведення співбесід та отримання відповідних сертифікатів, щоб люди розуміли, що запитувати, бо інколи їм дуже важко зорієнтуватися і таке приходить тільки з часом.
доречі, на позицію senior-a, мене часто запитували ViewController lifecycle 😁

можливо варто було б окремо проводити курси для навчання проведення співбесід та отримання відповідних сертифікатів

У мене в цьому році закрився топ-3 кінчених співбесід, то оцю третю проводили якраз «сертифіковані інтерв’юери» :) І сама кінчена частина була якраз у «практичній задачі», посони старались, зробили проект заглушку, але сам проект чомусь не запустився на робочій машині і по факту не можливо було його зробити за 15-20 хв (посони очікували скоріше за все, що кандидат все нагенерує через частГПТ).

Проблема в тому, що немає універсального алгоритму інтерв’ю. З іншого боку сама прикольна співбесіда, яка в мене була складалась з 2 алгоритмічних задач (для джава девелопера).
Підхід з пазлами, як у випадку Artem K, теж може спрацювати, якщо вміти його приготувати. Звісно те, що ви озвучили, — це найбезпечніший варіант, там складніше інтерв’юеру зробити помилку порівняно з підходом з пазлами чи алгоритмами.

Мистецтво (відділяємо від науки) проведення співбесід полягає в тому, щоб зрозуміти доцільність конкретного кандидата до конкретної вакансії за допомогою питань.

Ці питання (як і більшість подібних) скоріше для джунів, які ще толком не мають практики, але мають хорошу теоретичну базу вивчену за пару днів до співбесіди. Senior (на маю старечу думку) має знати весь зоопарк фреймворків потрібних для його домену, підводні камені (те про що не пише документація, але набуто з досвідом), де і як швидко знайти потрібну інформацію і швидко вміти вивчати нову інформацію.

З цих всіх питань — про queue, то таке. Тільки один раз при навантаженні треба було створити свою, і то тільки після піфоманс тестів. В більшості випадків достатньо було застосовувати звичайні механізми GCD. Зараз Apple створив ще один механізм Tasks.
Мене б більше цікавили речі, як реактів стек і взагалі завдяки чому все це працює і для чого. Далі можна спокійно про патерни «банди» поговорити і про архітектуру.
Cтосовно Viper — жодного разу не було потреби його застосовувати.
Цікаві питання бувають про computer science.
Якщо хтось просто задає питання на інтервʼю за документацією, все зрозуміло і можна закінчувати.

Дай божЕ відсотків 20 з цього запитують))
Інші 80 — субʼєктивне трактування SOLID, та абстрактні запитання. А інколи у інтервʼюера ще і застаріла інформація)
Декілька разів доводилося доказувати що не верблюд, бо супер-тімлід-архіиектор останній раз відкривав доку у 2010

А інколи сам факт доказу правоти своїх тверджень (у звісно ж у конструктивному руслі на конкретних прикладах) можуть трактувати як «конфліктність» і небажання «просто погодитись» з думкою «більш досвічених» колег ))

Про SOLID — то ви дарма так кажете. Насправді, треба не повторювати, як завєти ООП :)
А так, як приклад їх порушення. Простий приклад — як виявити у коді не хай Swift — порушення принципа Лісков.

Поясніть нотацію Big

сінйіор міг би сказати ще, що там асимптотичне наближення/не_вихід_вище/не_вихід_нижче до, при n->∞ (починаючи з якогось n) і що при малих значеннях n, — не факт що

сінйіор міг би сказати ще, що

...це питання перекладається як:
Продемонструйте, що нормально вчились в нормальному універі :)

Просто БігО вже всі завчили правильні відповіді, треба щось нове.

Назва топіку приблизно — що треба знати випускнику щоб вступити в політех, технікум або гівно

ібо неможливо співбесідою виявити уміння

А чим можно? Придуркуватими задачами з літкода?

неджуна — лише роботою над проєктом, а собес — це радше тест(чи людина підходить ментально і якщо підходить то в якій мірі: питається одне, сканується — інше), або ж ритуал соціальний(котрі часто непомітні саме як ритуали) — ібо «так прийнято» і свого роду адаптація під випробувальний а то як так без ритуалу прямо приймати в свою/"свою" когорту

Підписатись на коментарі