Основы Swift
Optional'ы, `let`/`var`, перечисления, коллекции, `guard`/`defer` и сопоставление с образцом — языковой фундамент, с которого начинается любое iOS-собеседование.
13 вопросов
JuniorТеорияОчень частоЧем guard let отличается от if let и когда guard читается лучше?
Чем guard let отличается от if let и когда guard читается лучше?
if let связывает развёрнутое значение только внутри своих скобок. guard let связывает в объемлющую область и обязывает else выйти (return, throw, break). guard лучше для ранней проверки-выхода, оставляя основной путь без вложенности.
Типичные ошибки
- ✗Думают, что привязка
if letвидна после блокаif - ✗Считают ветку
elseуguardнеобязательной - ✗Полагают, что
guard letиif letодинаково ограничивают область привязки
Уточняющие вопросы
- →Почему ветка
elseуguardобязана передать управление из области? - →Как
guard letдержит основной путь плоским против вложенныхif let?
JuniorТеорияОчень частоЧто такое optional в Swift и какой класс ошибок он устраняет на этапе компиляции?
Что такое optional в Swift и какой класс ошибок он устраняет на этапе компиляции?
Optional (Optional<Wrapped>, т.е. T?) либо содержит значение (.some), либо равен nil (.none). Он делает отсутствие явным в системе типов, поэтому компилятор заставляет обработать nil-случай — устраняя неожиданные краши от разыменования null.
Типичные ошибки
- ✗Думают, что optional подставляет значение по умолчанию вместо
nil - ✗Считают, что optional применим только к ссылочным или указательным типам
- ✗Путают optional с механизмом ошибок
throwsв Swift
Уточняющие вопросы
- →Во что разворачивается
Optional— что такое.someи.none? - →Почему компилятор отвергает использование
T?там, где ожидается обычныйT?
JuniorПроизводительностьЧастоКакова стоимость вставки элемента в начало Swift Array и почему?
Какова стоимость вставки элемента в начало Swift Array и почему?
Вставка по индексу 0 — это O(n): Swift Array хранит элементы непрерывно, поэтому каждый существующий элемент сдвигается на слот вправо. Добавление в конец — амортизированное O(1). При частой вставке в начало возьмите другую структуру или соберите в обратном порядке.
Типичные ошибки
- ✗Считают вставку в начало такой же дешёвой, как добавление в конец у
Array - ✗Думают, что
Arrayиспользует связанную структуру без сдвига - ✗Приписывают стоимость перевыделению, а не сдвигу элементов
Уточняющие вопросы
- →Почему
appendлишь амортизированное O(1), а не всегда O(1)? - →Какую структуру данных вы возьмёте при частой вставке в начало?
JuniorТеорияЧастоArray против Set против Dictionary — сравните стоимость поиска и упорядоченность, и что Set требует от своего элемента?
Array против Set против Dictionary — сравните стоимость поиска и упорядоченность, и что Set требует от своего элемента?
Array упорядочен по индексу — доступ O(1), поиск вхождения O(n). Set и Dictionary — неупорядоченные хеш-таблицы, средний O(1) для поиска. Set требует, чтобы элементы (а Dictionary — ключи) были Hashable — равные значения хешируются одинаково.
Типичные ошибки
- ✗Считают поиск вхождения в
Arrayза O(1), как уSet - ✗Думают, что
SetилиDictionaryсохраняет порядок вставки - ✗Полагают, что
SetтребуетComparableилиEquatable, а неHashable
Уточняющие вопросы
- →Почему хеширование даёт
Setсреднее O(1), а не гарантированное O(1)? - →Что ломается, если реализации
HashableиEquatableтипа расходятся?
JuniorКодЧастоПредскажите вывод в консоль для функции с двумя defer-блоками перед return.
Предскажите вывод в консоль для функции с двумя defer-блоками перед return.
defer-блоки выполняются при выходе из области — здесь на return — в порядке LIFO, последний зарегистрированный первым. Печатает body, затем B, затем A; возвращённое 42 — последним, после возврата load(). Вывод — body, B, A, 42.
Типичные ошибки
- ✗Ждут, что
defer-блоки выполнятся в порядке написания (FIFO), а не LIFO - ✗Думают, что
deferсрабатывает на своей строке, а не при выходе из области - ✗Считают, что обычный
returnпропускаетdefer-блоки
Уточняющие вопросы
- →В каком порядке выполнятся три
defer-блока в одной области? - →Выполнится ли
defer, если функция выходит, бросая ошибку?
JuniorТеорияЧастоRaw values против associated values в enum Swift — что хранит каждый и какой даёт init?(rawValue:) даром?
Raw values против associated values в enum Swift — что хранит каждый и какой даёт init?(rawValue:) даром?
Raw value — одна константа времени компиляции на case, все одного базового типа (Int, String, …), поэтому компилятор синтезирует failable init?(rawValue:). Associated value несёт разные данные времени выполнения на case. У case либо одно, либо другое, но не оба.
Типичные ошибки
- ✗Путают, какой вид хранит константы, а какой — данные времени выполнения
- ✗Думают, что один case может нести и raw value, и associated values
- ✗Считают, что
init?(rawValue:)получают все enum, а не только с raw values
Уточняющие вопросы
- →Почему
init?(rawValue:)failable, а не обычныйinit? - →Какие базовые типы допустимы для raw value у enum?
JuniorТеорияЧастоЧто такое implicitly unwrapped optional, почему его использует @IBOutlet и когда он крашится?
Что такое implicitly unwrapped optional, почему его использует @IBOutlet и когда он крашится?
Implicitly unwrapped optional (T!) force-unwrap'ится автоматически при каждом обращении. @IBOutlet берёт его, ведь view равно nil при init, но подключено до загрузки экрана. Крашится, если обратиться, пока оно ещё nil, например до viewDidLoad.
Типичные ошибки
- ✗Думают, что IUO возвращает
nil, а не крашится при обращении к пустому - ✗Считают
T!неопциональным типом без риска краша - ✗Путают implicitly unwrapped optional со свойством
lazy
Уточняющие вопросы
- →Почему outlet равен
nilмеждуinitиviewDidLoad? - →Когда вы замените IUO у
@IBOutletна обычный optional?
JuniorТеорияЧастоЧто гарантируют let и var, и почему let-структура ведёт себя иначе, чем let-ссылка на class?
Что гарантируют let и var, и почему let-структура ведёт себя иначе, чем let-ссылка на class?
let — константа, var — изменяемая переменная. У struct (значимого типа) let замораживает всё значение — нельзя ни переприсвоить, ни изменить свойство. У class (ссылочного типа) let фиксирует только ссылку; объект остаётся изменяемым.
Типичные ошибки
- ✗Думают, что
letу экземпляра class запрещает менять свойства объекта - ✗Считают
letпротивvarвыбором ради производительности, а не контрактом изменяемости - ✗Полагают, что
let-структура ведёт себя какlet-класс с неизменяемостью только ссылки
Уточняющие вопросы
- →Почему через
var-структуру можно вызвать mutating-метод, а черезlet-структуру — нет? - →Что замораживает
let, если структура держит ссылку на class как свойство?
JuniorТеорияЧастоПеречислите способы развернуть optional в Swift и упорядочьте их по безопасности.
Перечислите способы развернуть optional в Swift и упорядочьте их по безопасности.
Безопасно — if let/guard let связывают локально, ?? даёт значение по умолчанию, ?. даёт nil при любом nil-звене, case let разбирает. Небезопасно — force-unwrap ! крашится на nil. Предпочитайте связывающие формы; ! — для заведомо не-nil.
Типичные ошибки
- ✗Считают, что
?.илиif letмогут крашнуться так же, как force-unwrap! - ✗Ставят force-unwrap
!в безопасные из-за его краткости - ✗Забывают, что
??и сопоставление с образцом — тоже средства развёртывания
Уточняющие вопросы
- →Когда force-unwrap
!— оправданный выбор вместо связывающей формы? - →Чем
?.отличается отif let, когда несколько звеньев могут бытьnil?
MiddleКодЧастоРазберите enum с associated values через switch с привязкой значения и where-условием
Разберите enum с associated values через switch с привязкой значения и where-условием
Сопоставляйте каждый case с привязкой значения — case let .deposit(amount) — и добавьте where для уточнённой ветки, например case let .deposit(a) where a > 1000. Так как enum конечен, исчерпывающий switch не требует default и укажет на любой необработанный новый case.
Типичные ошибки
- ✗Думают, что associated значения нельзя связать прямо в образце
case - ✗Считают, что исчерпывающий
switchпо enum всё равно требуетdefault - ✗Ставят
where-условие не туда или принимают его за цикл
Уточняющие вопросы
- →Что делает компилятор, когда вы добавляете case к исчерпывающе разобранному enum?
- →Чем
case letотличается от привязки каждого associated значения по отдельности?
MiddleТеорияЧастоВ a?.b?.c ?? d разберите вычисление — где цепочка останавливается и где это всё ещё может крашнуться?
В a?.b?.c ?? d разберите вычисление — где цепочка останавливается и где это всё ещё может крашнуться?
Optional chaining вычисляется слева направо и коротко замыкается — если a равно nil, всё a?.b?.c даёт nil, не трогая b/c. Результат optional, поэтому ?? d даёт d при nil. Цепочка не крашится, но d или force-unwrap внутри — могут.
Типичные ошибки
- ✗Думают, что nil-звено в середине крашится, а не коротко замыкается на
nil - ✗Считают, что
?? dловит краши force-unwrap или subscript внутри цепочки - ✗Полагают, что цепочка вычисляет каждое звено, несмотря на ранний
nil
Уточняющие вопросы
- →Каков статический тип
a?.b?.cдо применения?? d? - →Как добавление
!или subscript[i]внутрь цепочки вернёт риск краша?
JuniorТеорияИногдаПочему рекурсивному enum в Swift нужно ключевое слово indirect?
Почему рекурсивному enum в Swift нужно ключевое слово indirect?
Enum — значимый тип, хранящийся inline, поэтому case, содержащий тип самого enum, требовал бы бесконечного размера. indirect велит хранить значение такого case за указателем (в куче), а не inline, разрывая рекурсию размера. Помечают один case или весь enum.
Типичные ошибки
- ✗Думают, что рекурсивный enum компилируется без
indirect - ✗Считают, что
indirectделает enum ссылочным типом с общим хранилищем - ✗Полагают, что
indirect— флаг производительности, а не требование размещения
Уточняющие вопросы
- →Какова стоимость
indirect-case во время выполнения? - →Можно ли пометить
indirectтолько один case вместо всего enum?
JuniorТеорияИногдаКакие задачи решают typealias и tuple, и что tuple НЕ может, а struct может?
Какие задачи решают typealias и tuple, и что tuple НЕ может, а struct может?
typealias даёт существующему типу читаемое второе имя — нового типа не создаёт. Tuple группирует несколько значений в лёгкий безымянный ad-hoc тип. В отличие от struct, tuple не соответствует протоколам, не содержит методы и не расширяется.
Типичные ошибки
- ✗Думают, что
typealiasвводит новый, отдельный тип - ✗Считают, что tuple может соответствовать протоколу или получить методы
- ✗Полагают, что tuple и struct полностью взаимозаменяемы
Уточняющие вопросы
- →Почему два имени
typealiasдля одного типа взаимозаменяемы? - →Когда
structлучше, чем возврат tuple?