Вопросы на собеседовании: iOS-разработчик
100 реальных вопросов с образцовыми ответами и пояснениями для уровня Middle.
Смотреть пример резюме: iOS-разработчик →Тренировка флешкарточками
Интервальное повторение · Hunter Pass
Вопросы
View в SwiftUI является временным значением, описывающим интерфейс, а долгоживущее состояние хранится вне отдельного экземпляра этого значения.
- SwiftUI может создавать новые значения View при изменении входных данных, поэтому struct View нельзя считать постоянным объектом UI.
- Property wrappers связывают описание с хранилищем, которым владеет фреймворк, другой объект или environment.
- Изменение состояния инвалидирует зависимые части иерархии и заставляет SwiftUI заново вычислить их body.
Зачем это спрашивают: Интервьюер проверяет понимание декларативного рендеринга, а не отношение к SwiftUI View как к контроллеру UIKit.
@State связывает с идентичностью View локальное хранилище небольшого изменяемого значения, которое служит источником истины.
- SwiftUI владеет хранилищем и сохраняет его между новыми экземплярами одной идентичности View.
- Изменение wrapped value инвалидирует вычисления body, которые его читают.
- Свойство стоит делать private, если оно является деталью реализации, а его начальное значение не следует использовать как внешний канал синхронизации.
Зачем это спрашивают: Сильный ответ объясняет хранилище под управлением фреймворка, идентичность View и то, почему @State не подходит для общего состояния.
@Binding предоставляет доступ на чтение и запись к состоянию, которым владеет другой объект, не принимая владение его хранилищем.
- Родитель обычно передаёт дочернему View $property, если тот должен менять значение.
- Чтение binding возвращает текущее исходное значение, а запись обновляет тот же источник.
- Binding также можно создать с собственными замыканиями get и set, чтобы адаптировать существующую модель состояния.
Зачем это спрашивают: Интервьюер проверяет, отличаете ли вы заимствованный доступ к изменению от владения состоянием.
ObservableObject предоставляет уведомления об изменениях, а @Published отправляет их перед изменением отмеченного свойства.
- ObservableObject предоставляет publisher objectWillChange, который используется наблюдением SwiftUI.
- @Published создаёт publisher отдельного свойства и одновременно участвует в общем сигнале изменения объекта.
- Для UI изменения должны происходить на подходящем actor, поэтому наблюдаемую модель часто изолируют с помощью @MainActor.
Зачем это спрашивают: Хороший ответ связывает инвалидацию объекта, publishers свойств и изоляцию UI.
@ObservedObject предполагает, что View наблюдает за ObservableObject, за время жизни которого отвечает внешний владелец.
- Wrapper подписывает View на уведомления об изменении объекта.
- Пересоздание View не обеспечивает стабильное владение объектом, инициализированным заново.
- Родительская инъекция или другой внешний владелец должны сохранять наблюдаемый экземпляр в течение нужного времени.
Зачем это спрашивают: Интервьюер оценивает, отделяете ли вы наблюдение от создания объекта и владения им.
@StateObject сохраняет ObservableObject, когда идентичность View создаёт этот объект и владеет им.
- SwiftUI инициализирует объект один раз для этой идентичности, а не при каждом новом создании значения View.
- Уведомления об изменении объекта инвалидируют зависимый UI так же, как с @ObservedObject.
- Хранилище прекращает существование, когда владеющая идентичность View покидает иерархию.
Зачем это спрашивают: Сильный ответ связывает @StateObject со стабильным владением ссылочным объектом и идентичностью View.
Замена @StateObject на @ObservedObject убирает управляемое SwiftUI владение объектом со стороны View.
- Объект, созданный прямо в @ObservedObject, может пересоздаваться при появлении новых значений View.
- Повторная инициализация может сбросить состояние и заново запустить подписки или другую начальную работу.
- Если экземпляр уже передаёт внешний владелец, @ObservedObject является правильным выбором, потому что второй владелец исказил бы поток данных.
Зачем это спрашивают: Интервьюер проверяет, можете ли вы выбирать wrapper на основе времени жизни, а не синтаксиса.
@EnvironmentObject получает общий ObservableObject, который предок передал через environment SwiftUI.
- Он избавляет от явной передачи одного объекта через каждый промежуточный View.
- Поиск выполняется по типу, поэтому в иерархии должна существовать подходящая инъекция environmentObject.
- Wrapper подходит действительно общим зависимостям, но чрезмерное использование скрывает требования View и усложняет previews или тесты.
Зачем это спрашивают: Хороший ответ охватывает поиск по типу и компромисс между удобной передачей и явными зависимостями.
@Environment читает значение по ключу environment, а @EnvironmentObject находит наблюдаемый ссылочный объект по типу.
- К environment values относятся системные входные данные, например locale, color scheme и действие dismiss.
- Собственные определения EnvironmentKey могут передавать небольшие значения или зависимости.
- @EnvironmentObject специально наблюдает за внедрённым ObservableObject и требует наличия этого объекта выше в иерархии.
Зачем это спрашивают: Интервьюер проверяет, отличаете ли вы environment values с поиском по ключу от наблюдаемых объектов с поиском по типу.
SwiftUI связывает хранилище состояния со структурной или явной идентичностью View, а не с конкретным экземпляром структуры.
- При сохранении идентичности хранилище @State и @StateObject переживает повторное вычисление body.
- Изменение идентичности из-за другой ветки, позиции или id может удалить старое хранилище и создать новое.
- Поэтому стабильная идентичность влияет на правильность списков, условных иерархий и дочерних Views с состоянием.
Зачем это спрашивают: Сильный ответ объясняет сбросы состояния правилами идентичности, а не произвольными перерисовками.
Publisher создаёт типизированную последовательность, Subscriber получает её, а Subscription связывает их и управляет спросом и отменой.
- Publisher объявляет типы Output и Failure.
- Subscriber получает подписку, затем от нуля до нескольких значений и не более одного сигнала завершения.
- Subscription позволяет подписчику запрашивать нужное число значений и отменять дальнейшую доставку.
Зачем это спрашивают: Интервьюер проверяет, знаете ли вы роли основных протоколов Combine, а не только удобные операторы.
Каждый Publisher в Combine указывает тип отправляемых значений и тип ошибки, с которой он может завершиться.
- Publisher, который не может завершиться ошибкой, использует Never как тип Failure.
- Операторы должны сохранять или намеренно преобразовывать совместимые типы Output и Failure.
- mapError, setFailureType, catch и replaceError адаптируют канал ошибок для следующих звеньев цепочки.
Зачем это спрашивают: Хороший ответ показывает рассуждение на уровне типов о значениях и завершающих ошибках pipeline.
AnyCancellable представляет владение отменой, и его освобождение отменяет связанную подписку.
- sink и assign обычно возвращают AnyCancellable для созданного подписчика.
- Хранение в свойстве или Set поддерживает активность подписки в течение жизни владельца.
- Явная отмена или освобождение хранилища прекращает дальнейшую доставку и разрывает цепочку подписки.
Зачем это спрашивают: Интервьюер оценивает, понимаете ли вы время жизни подписки как обычное владение.
PassthroughSubject передаёт только новые значения, а CurrentValueSubject также хранит и предоставляет текущее значение.
- Новый подписчик PassthroughSubject ничего не получает до следующего send.
- Новый подписчик CurrentValueSubject сразу получает текущее значение, а затем последующие обновления.
- Оба типа позволяют императивно отправлять значения и сигнал завершения в декларативную цепочку.
Зачем это спрашивают: Сильный ответ отличает поток событий от потока, похожего на состояние.
map напрямую преобразует каждое значение, а flatMap превращает каждое значение в новый Publisher и объединяет эти внутренние потоки.
- map сохраняет отношение одного значения к одному и не вводит новый асинхронный поток.
- flatMap полезен, когда входное значение запускает работу, представленную Publisher.
- flatMap не отменяет старые внутренние publishers автоматически, поэтому switchToLatest выбирают отдельно, когда активным должен оставаться только новейший поток.
Зачем это спрашивают: Интервьюер проверяет понимание разворачивания publishers и отсутствие путаницы flatMap с обычным преобразованием значения.
combineLatest отправляет сочетание последних значений после первого значения от каждого источника, а zip соединяет значения по порядку их появления.
- combineLatest реагирует на обновление любого upstream и повторно использует последнее значение другого.
- zip ждёт по одному несопоставленному значению с каждой стороны и потребляет их как пару.
- Выбор зависит от того, представляет ли связь текущее состояние или попарное сочетание событий.
Зачем это спрашивают: Хороший ответ объясняет семантику отправки, а не просто называет два оператора объединения.
subscribe(on:) влияет на место создания подписки и выполнения upstream-работы, а receive(on:) меняет scheduler для downstream-доставки.
- subscribe(on:) влияет на операции подписки, запроса значений и отмены выше по цепочке.
- receive(on:) доставляет последующие значения и завершение на указанном scheduler.
- Этапы, работающие с UI, обычно получают значения на main scheduler, но лишние переключения scheduler не стоит разбрасывать по цепочке.
Зачем это спрашивают: Интервьюер проверяет, умеете ли вы размещать границы планирования с учётом upstream-работы и downstream-потребителей.
eraseToAnyPublisher скрывает конкретную цепочку publisher за стабильным типом AnyPublisher.
- Цепочки операторов имеют длинные обобщённые типы, зависящие от реализации, которые обычно не нужны вызывающему коду.
- Стирание типа не позволяет изменениям реализации проникать в сигнатуру API.
- Оно сохраняет типы Output и Failure, но убирает доступ к операциям конкретного publisher.
Зачем это спрашивают: Сильный ответ связывает стирание типа с границами API, а не считает его обязательным для каждого pipeline.
async отмечает функцию, которая может приостановиться, а await отмечает вызов, где текущая задача может приостановиться до получения асинхронного результата.
- Приостановка задачи не блокирует лежащий в основе поток во время ожидания.
- Async-функция может вернуть значение или выбросить ошибку, а сочетание возможностей записывается как async throws.
- await отмечает возможную точку приостановки, но не гарантирует ни фактическую приостановку, ни переключение потока.
Зачем это спрашивают: Интервьюер проверяет, отличаете ли вы приостановку task от блокировки и планирования потока.
async let создаёт дочернюю задачу в текущей области видимости для значения, которое можно вычислять одновременно с другой дочерней работой.
- Несколько объявлений async let запускают дочерние операции до ожидания их значений.
- Чтение значения требует await и передаёт ошибку, если дочернее выражение может её выбросить.
- Незавершённые дочерние задачи автоматически ожидаются или отменяются до выхода из внешней области видимости.
Зачем это спрашивают: Хороший ответ определяет async let как structured concurrency для фиксированного числа дочерних результатов.
Закрытые вопросы
- 21
Чем withTaskGroup отличается от async let?
asyncconcurrency - 22
Какой контекст наследует Task { } в Swift concurrency?
concurrencyswiftownership - 23
Чем Task.detached отличается от Task { }?
- 24
Как работает отмена в Swift concurrency?
concurrencyswiftresilience - 25
Какую гарантию изоляции предоставляет actor в Swift?
swiftconcurrency - 26
Что гарантирует @MainActor?
- 27
Что такое reentrancy у actor в Swift?
swiftconcurrency - 28
Что означает Sendable в Swift concurrency?
concurrencyswift - 29
Как Automatic Reference Counting управляет памятью в Swift?
memoryswift - 30
Чем типы-значения отличаются от ссылочных типов с точки зрения владения?
value-semanticsownership - 31
Как возникает цикл сильных ссылок?
memory - 32
Какой семантикой обладает weak-ссылка в Swift?
swift - 33
Чем unowned отличается от weak в Swift?
memoryswift - 34
Как escaping closure может создать retain cycle со своим владельцем?
closuresmemory - 35
Почему свойства delegate обычно объявляют weak?
delegationprotocols - 36
Что представляют собой Encodable, Decodable и Codable?
codable - 37
Когда компилятор может синтезировать реализацию Codable?
codable - 38
Чем отличаются keyed, unkeyed и single-value containers в Codable?
containerscodable - 39
Чем serial DispatchQueue отличается от concurrent DispatchQueue?
concurrency - 40
Чем sync dispatch отличается от async dispatch в GCD?
asyncconcurrency - 41
Что предоставляют DispatchGroup и quality of service очереди?
data-structures - 42
Что означает protocol-oriented design в Swift?
typingswiftprotocols - 43
Чем отличается dispatch для требований протокола и методов только из extension?
typingprotocols - 44
Что такое associated type в протоколе Swift?
typingswiftprotocols - 45
Как ограничения типов и where clauses улучшают обобщённый API в Swift?
genericsswiftapi - 46
Чем some Protocol отличается от any Protocol в Swift?
typingswiftprotocols - 47
Какую проблему решает type erasure в Swift?
swift - 48
Какая ответственность принадлежит Model, View и ViewModel в MVVM?
architecture-patterns - 49
Как SwiftUI View может наблюдать за MVVM ViewModel?
swiftuiswiftarchitecture-patterns - 50
Чем не должна владеть MVVM ViewModel?
architecture-patterns - 51
Как бы вы реализовали экран списка товаров для состояний загрузки, заполненного и пустого списка, а также ошибки?
- 52
Как бы вы добавили в SwiftUI поиск с задержкой ввода и не показывали устаревшие результаты?
debounceswiftuiswift - 53
Как бы вы реализовали надёжную бесконечную прокрутку ленты?
- 54
Как бы вы построили список с офлайн-режимом на основе Core Data?
core-data - 55
Как бы вы разрешили конфликт офлайн-изменений, если та же запись изменилась на сервере?
- 56
Как бы вы реализовали загрузку изображений для быстро прокручиваемой ленты?
- 57
Как бы вы реализовали многошаговую форму SwiftUI с проверкой данных и отправкой?
formsswiftuivalidation - 58
Как бы вы реализовали deep link на вложенный экран, требующий авторизации?
- 59
Как бы вы открывали нужный раздел по push-уведомлению при разных состояниях жизненного цикла приложения?
- 60
Как бы вы добавили оптимистичное обновление кнопки избранного в SwiftUI?
swiftuilockingswift - 61
Дочерний экран SwiftUI теряет состояние ViewModel при обновлении родительского экрана; как бы вы нашли и исправили проблему?
swiftuiswift - 62
Как бы вы разделили редактируемое состояние между несколькими экранами SwiftUI без появления несогласованных копий?
swiftuiswift - 63
Где бы вы запускали асинхронные побочные эффекты на экране SwiftUI?
asyncswiftconcurrency - 64
Как бы вы обеспечили потокобезопасность ViewModel в SwiftUI при загрузке и преобразовании данных?
concurrencyswiftswiftui - 65
Как бы вы восстановили SwiftUI NavigationStack после перезапуска приложения?
swiftuinavigationswift - 66
Как бы вы встроили существующий контроллер UIKit в SwiftUI и сделали обновления предсказуемыми?
uikitswiftuilearning - 67
Как бы вы встроили функцию на SwiftUI в существующий навигационный сценарий UIKit?
uikitswiftuinavigation - 68
Как бы вы проверили доступность при реализации собственного элемента управления в SwiftUI?
a11yswiftuiswift - 69
Закрытый экран не деинициализируется; как бы вы нашли retain cycle?
memory - 70
Как бы вы отлаживали retain cycle с Combine sink во ViewModel?
combinememory - 71
Сбой в рабочей версии приложения виден только в несимволицированных отчётах; что бы вы сделали?
- 72
Как бы вы исследовали сбой EXC_BAD_ACCESS в коде Swift?
swift - 73
Как бы вы диагностировали обновления UIKit вне главного потока?
uikitconcurrency - 74
Как бы вы исследовали гонку данных в общем кэше в памяти?
cachingmemory - 75
Как бы вы отлаживали периодический сбой index out of range в представлении коллекции?
viewsindexes - 76
Пользователи сообщают, что приложение зависает на несколько секунд без сбоя; как бы вы это исследовали?
scheduling - 77
Как бы вы отлаживали взаимную блокировку между старым кодом GCD и кодом с @MainActor?
lockingconcurrency - 78
Как бы вы профилировали и снижали высокую загрузку процессора в функции iOS-приложения?
- 79
Список UIKit плохо прокручивается при появлении изображений и текста; как бы вы его оптимизировали?
uikitoptimization - 80
Как бы вы снизили скачки потребления памяти при загрузке большого числа фотографий?
memory - 81
Экран SwiftUI слишком часто пересчитывается и перерисовывается; как бы вы нашли и сократили лишние обновления?
swiftuiswift - 82
Как бы вы ускорили медленный холодный запуск приложения?
- 83
Как бы вы спроектировали тестируемый сетевой слой на URLSession с типизированными конечными точками?
designendpointsnetworking - 84
Как бы вы предотвратили одновременное обновление истёкшего токена доступа несколькими неудачными запросами?
tokens - 85
Как бы вы определяли, какие сетевые запросы можно автоматически повторять?
- 86
Как бы вы передали отмену с исчезнувшего экрана SwiftUI в URLSession?
swiftuiresilienceswift - 87
Как бы вы преобразовывали сетевые сбои в полезные для пользователя ошибки?
- 88
Как бы вы добавили HTTP-кэширование в клиент на URLSession?
cachinghttpnetworking - 89
Как бы вы внедряли разные сетевые реализации в функции iOS-приложения без глобального экземпляра?
- 90
Как бы вы отлаживали запрос API, который работает в браузере, но завершается ошибкой в iOS-приложении?
api - 91
Как бы вы написали модульный тест для MVVM ViewModel с @MainActor?
architecture-patternsunit - 92
Как бы вы тестировали отмену и завершение асинхронных операций не по порядку?
asyncswiftconcurrency - 93
Когда в тестах вы бы использовали URLProtocol вместо тестовой реализации сетевого протокола?
typingprotocolstesting - 94
Как бы вы тестировали репозиторий Core Data, не затрагивая постоянные данные пользователя?
core-data - 95
Как бы вы сделали UI-тест XCTest для входа в приложение менее нестабильным?
flaky - 96
Как бы вы выбрали тесты для новой функции офлайн-поиска?
testing - 97
Тест проходит локально, но периодически завершается ошибкой в CI; как бы вы его исследовали?
- 98
Как бы вы добавили регрессионный тест для сбоя в рабочей версии из-за некорректных данных API?
regressionapi - 99
Как бы вы ускорили медленный набор iOS-тестов без потери полезного покрытия?
coverage - 100
Как бы вы спроектировали и проверили офлайн-ленту SwiftUI с пагинацией, изображениями и deep link?
swiftuidesignvalidation