Що відповідати, щоб пройти співбесіду на Senior iOS Developer в Instagram, Viber або Дію
Вітаю, колеги! Мене звати Дмитро. З 2012 року я професійно займаюся 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. Але все ж обговорення конкретних запитань та тем зі співбесід є корисним, тому що це формує уявлення, на що звернути увагу. Сподіваюся, цей блог підсвітить, в яких напрямках вам слід працювати над своїми знаннями та навичками, щоб перейти на наступну сходинку в кар’єрі.
16 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів