Топ 20 вопросов на iOS собеседовании. Уровень Junior

Давайте рассмотрим самые популярные вопросы на собеседовании iOS, которые часто задаются разработчику Junior уровня. К каждому вопросу приведен подробный ответ, но будет плюсом читателю самостоятельно углубиться в каждую из тем, которые рассматриваются в статье.

1. Что такое Retain Cycle? Каким образом исправляется проблема Retain Cycle?

Retain Cycle — распространенная проблема, связанная с управлением памяти, когда сущность A обладает сущностью B и наоборот. Данную проблему называют еще циклом сильных ссылок.

Для исправления подобной проблемы, сделайте одну из ссылок weak или unowned.

Дополнительное чтение: Retain Cycle

2. Что такое ARC? В чем разница между ARC и MRC?

Каждый объект содержит некий счетчик, отображающий количество объектов, которые на него ссылаются (ссылки). При равенстве счетчика нулю — объекты освобождаются из памяти.

В ситуации, когда объект освобождается, но счетчик ссылок остается не равным нулю, два объекта содержат сильные ссылки друг на друга, получается цикличность ссылок (retain cycle

MRC — Manual Reference Counter (ручное управление счетчиком ссылок). Значит нужно самостоятельно добавлять в код вызов функций alloc, retain, release, dealloc для управления счетчика.

ARC — Automatic Reference Counting (автоматический подсчет ссылок). То же, что и MRC, но компилятор сам расставляет методы retain или release за программиста. Важно понимать, что ARC работает при компиляции, а подсчет ссылок — в рантайме.

Дополнительное чтение: Что такое ARC (Automatic Reference Counting)?

3. Чем отличается стек от кучи?

Этот вопрос на собеседовании — классика. Для ответа на него, сначала разберем, что такое Value Type и Reference Type.

Value Type — тип значения, при котором каждый экземпляр хранит уникальную копию собственных данных. В этом случае происходит создание нового экземпляра (копии) при присвоении значения переменной или константе, либо при передаче экземпляра в функцию.

Reference Type — ссылочный тип, каждый экземпляр которого использует одну копию данных. В этом случае сохраняется или возвращается ссылка на тот же экземпляр при присвоении значения переменной или константе, либо, при передаче экземпляра в функцию.

Экземпляры ссылочного типа, такие как функции или классы, хранятся в управляемой динамической памяти — куче (heap), в то время как экземпляры типа значения, такие как структура или массив расположены в области памяти, которая называется стеком (stack).

В случае если экземпляр типа значения является частью экземпляра ссылочного типа, то значение сохраняется в куче вместе с экземпляром ссылочного типа.

Пример: структура сама по себе хранится в стеке, но если эта структура расположена в классе, то поскольку класс хранится в куче, то и структура сохраняется в куче.

Дополнительное чтение: Value Type и Reference Type или чем стек отличается от кучи?

4. Чем отличается frame от bounds?

Frame задается относительно собственного superview, Bounds — относительно собственной координатной системы.

Пример:

При создании view по координатам (x: 0, y: 0) и (width: 50, height: 50), frame и bounds будут одинаковы.

let view = UIView()
view.frame = CGRect(x: 0, y: 0, width: 50, height: 50)

print(view.frame) // (0.0, 0.0, 50.0, 50.0)
print(view.bounds) // (0.0, 0.0, 50.0, 50.0)

Попробуем изменить координаты view на (x: 100, y: 0): frame изменится, а bounds — нет.

let view = UIView()
view.frame = CGRect(x: 100, y: 0, width: 50, height: 50)

print(view.frame) // (100.0, 0.0, 50.0, 50.0)
print(view.bounds) // (0.0, 0.0, 50.0, 50.0)

В случае если трансформируете view, например, поворачиваете или увеличиваете масштаб, то frame меняется, но bounds остается неизменной.

let view = UIView()
view.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
view.transform = CGAffineTransform(scaleX: 2, y: 2)

print(view.frame) // (-25.0, -25.0, 100.0, 100.0)
print(view.bounds) // (0.0, 0.0, 50.0, 50.0)

Дополнительное чтение: Difference between Frame and Bounds in Swift

5. Какие состояния (states) встречаются у приложения?

Похожий вопрос задают часто — на одной трети собеседований, поэтому включаем и данную тему в наш топ вопросов.

В 2022 году приложение содержит пять состояний, которые выполняют подготовительную работу к тому или иному действию. Например, подготовка приложения к завершению работы или переключению процессов при работе в фоновом режиме.

Изначально приложение не запущено и работает в состоянии Non-running. После запуска пользователем, приложение переходит в состояние Foreground, в котором становится сначала Inactive — на этом этапе выполняется код программы, но не обрабатываются события интерфейса пользователя (интерфейс не отображается, касания не обрабатываются и т.д.).

Затем приложение переходит в состояние Active, в котором выполняется код и обрабатываются каждое событие UI. Если пользователь свернет приложение или переключится на другое, то наше приложение сначала перейдет в состояние Inactive и затем в состояние Background. В этом состоянии код выполняется ограниченное время (без дополнительного запроса), события UI не обрабатываются.

После состояния Background, приложение переходит в состояние Suspended. В этом состоянии код приложения не выполняется, а система, в качестве оптимизации памяти, способна самостоятельно завершить ваше приложение.

Какие состояния (states) бывают у приложения? - Junior вопросы

Non-running — приложение не запущено.

Inactive — приложение работает в Foreground, но не получает события. iOS приложение переходит в состояние Inactive когда поступает событие звонка или SMS-сообщения.

Active — приложение работает в Foreground (на переднем плане) и получает события.

Background — приложение работает в Background (в фоновом режиме) и выполняет код.

Suspended — приложение находится в Background, но код уже не выполняется. Система может завершить ваше приложение для оптимизации памяти.

Дополнительное чтение: Managing Your App’s Life Cycle

6. Что означают принципы проектирования KISS/DRY/YAGNI?

KISS — не усложняй. Принцип KISS утверждает, что большинство систем работают лучше без усложнений. Поэтому в области проектирования, простота относится, в том числе к главной цели разработчика и выражается в составлении программ без лишних сложностей.

DRY — не повторяй себя. Принцип DRY формулируется как: «Каждая часть знания должна иметь единственное, непротиворечивое и авторитетное представление в рамках системы».

YAGNI — вам это не понадобится. YAGNI — процесс и принцип проектирования ПО, к главной цели которого относится:

  • Отказ от избыточной функциональности;
  • Отказ добавления функциональности, в которой нет надобности.

7. Что такое SOLID?

SOLID состоит из пяти принципов проектирования (по одному на каждую букву), которые направлены на то, чтобы сделать код понятным, гибким и удобным для сопровождения.

S (The Single Responsibly Principle) – принцип единственной ответственности. У каждого класса только одна обязанность.

O (The Open Closed Principle) – принцип открытости или закрытости. Класс открыт для расширения, но закрыт для модификации.

L (The Liskov Substitution Principle) – принцип подстановки Барбары Лисков. Дочерний класс не нарушает определения типов родительского класса.

I (The interface Segregation Principle) – принцип разделения интерфейса. Разделяя интерфейс, разработчик решает проблему с одним толстым интерфейсом.

D (The dependency Inversion Principle) – принцип инверсии зависимостей. Слои высокого уровня в приложении, такие как контроллер представления, не должны напрямую зависеть от вещей низкого уровня, таких как сетевой компонент. Вместо этого, он зависит от абстракции этого компонента.

Дополнительное чтение: An iOS Engineer’s Perspective on SOLID Principles

8. В чем разница sync и async?

Не было ни одного собеседования, где бы не спросили о многопоточности. Уделим внимание этой теме.

sync — возвращает управление на текущую очередь только после завершения задания, async — возвращает управление на текущую очередь немедленно после запуска задания, не ожидая завершения.

Дополнительное чтение: DispatchQueue.main.async и DispatchQueue.main.sync

9. Closure – Value или Reference Type?

Вспомним определения Value Type и Reference Type:

Value Type (Тип значения) — каждый экземпляр хранит уникальную копию собственных данных. Происходит создание нового экземпляра (копии) при присвоении значения переменной или константе, либо при передаче экземпляра в функцию. К типу значения относятся: Int, Double, String, Array, Dictionary, Set, Struct, Enum и Tuple.

Reference Type (Ссылочный тип) — каждый экземпляр использует одну копию данных. То есть сохраняется или возвращается ссылка на тот же экземпляр при присвоении значения переменной или константе, либо при передаче экземпляра в функцию. К ссылочному типу относятся: Функции, Замыкания и Классы.

Дополнительное чтение: Value Type и Reference Type или чем стек отличается от кучи?

10. В чем разница между уровнем доступа Fileprivate и Private?

На текущий момент существует пять уровней доступа: Open, Public, Internal, Fileprivate и Private. Путаница возникает с первыми и последними двумя доступами. Рассмотрим последние два уровня:

Fileprivate — на этом уровне расположен доступ к элементам данных и функциям текущего файла. Используется для скрытия реализации, требуемой только в текущем исходном файле.

Private — самый низкий уровень доступа. Ограничивает использование сущности, которая включается декларацией или расширением в текущем файле. При этом доступ в подклассах или в других файлах отсутствует.

Private — разрешить доступ к членам данных и функциям в рамках их объявления или расширения в текущем файле

Fileprivate — разрешить доступ к членам данных и функциям в одном и том же исходном файле или в подклассе, или расширении.

Дополнительное чтение: Уровни доступа в Swift

11. Что такое Optional?

На собеседовании сотрудники компании иногда просят рассказать, как разворачивается Optional — будьте к этому готовы.

Optional или опционал — тип, который используется в Swift каждый раз при работе с необязательным значением. Замечали, как система типов в Swift показывает Optional тип? — через знак вопроса (?). Это сокращенная форма Optional. Так, например, следующие две записи идентичны:

let optionType: Int? = Int("10")
let optionType: Optional<Int> = Int("10")

Сам же тип Optional представляет собой перечисление с двумя состояниями .none и .some (Wrapped):

public enum Optional<Wrapped> : ExpressibleByNilLiteral {

    /// The absence of a value.
    ///
    /// In code, the absence of a value is typically written using the `nil`
    /// literal rather than the explicit `.none` enumeration case.
    case none

    /// The presence of a value, stored as `Wrapped`.
    case some(Wrapped)

    /// Creates an instance that stores the given value.
    public init(_ some: Wrapped)

    /// Creates an instance initialized with `nil`.
    ///
    /// Do not call this initializer directly. It is used by the compiler
    // when you initialize an `Optional` instance with a `nil` literal.
    public init(nilLiteral: ())

    ...
}

Дополнительное чтение: Optional

12. Чем отличается NSInteger от NSNumber?

Вопрос из секции Objective-C. Да, сегодня на собеседовании это еще важно. Совет: спрашивайте перед собеседованием, знание какого языка нужно кандидату.

NSNumber — объект и ссылочный тип, поддерживающий сериализацию и который разрешено записывать в массивы и т.д., плюс у него много методов для конвертации в другие типы данных.

NSInteger — традиционный int в C (typedef).

Дополнительное чтение: NSNumber и NSInteger

13. Чем отличается NSSet от NSArray?

Пока затронули Objective-C, давайте рассмотрим еще один популярный вопрос (аналогичный вопрос часто спрашивают по Swift секции).

Ответ на вопрос прост: в отличие от NSArray, NSSet хранит только уникальные объекты. Также, NSSet — хранение неупорядоченной коллекции, NSArray — упорядоченной коллекции.

Если на собеседовании необходимы дополнительные знания по рассматриваемому вопросу, сравните разницу скорости между ними.

Материал на эту тему доступен для чтения здесь: NSArray or NSSet, NSDictionary or NSMapTable.

Если говорить еще о сравнении между этими типами, то в NSArray объект получают по индексу, а в NSSet — путем сравнения объектов.

Дополнительное чтение: NSArray и NSSet

14. Назовите методы жизненного цикла ViewController

Часто, так или иначе, интервьюер затрагивает тему Lifecycle ViewController. В случае если кандидат путает порядок главных методов, в этом нет ничего страшного, но если кандидат путает сами значения методов или вовсе не владеет информацией — дальнейшее прохождение собеседования ставится под вопрос.

ViewDidLoad — метод вызывается, когда view уже создано. Метод вызывается только один раз за время существования ViewController.

ViewWillAppear — метод вызывается каждый раз перед тем, как появится ViewController. Этот метод может быть вызван несколько раз для одного экземпляра ViewController.

ViewDidAppear — метод вызывается каждый раз после появления ViewController.

ViewWillDisappear — метод вызывается перед удалением ViewController из иерархии представлений.

ViewDidDisappear — метод вызывается после удаления ViewController из иерархии представлений.

Чтобы ответить на текущий вопрос, рассказ об указанных методах будет достаточным (лучше излагать материал в том порядке, в котором он представлен в этой статье или на картинке ниже), но дополнительно не будет лишним рассказать и о других методах жизненного цикла, например, loadView или init.

Вопрос на собеседовании: Жизненный цикл ViewController

Дополнительное чтение: Жизненный цикл UIViewController’a

15. Чем Point (pt) отличается от Pixel (px)?

Pixel — точка на экране, а Point — плотность точки на экране.

Дополнительное чтение: Getting Pixels onto the Screen

16. Что такое полиморфизм?

По личному опыту при ответе на такой вопрос собеседования сотрудники компании чаще просят перечислить главные принципы ООП, но иногда спрашивают сами определения.

Полиморфизм — это способность объекта использовать методы производного класса, который не существует на момент создания базового

17. В чем разница между классом и структурой?

Структуры передаются по значению, а классы — по ссылке. Важно понимать, что экземпляры структур создаются в стеке, а экземпляры классов — только в куче.

Подробнее читайте здесь.

Отвечаем на следующий вопрос: когда используется класс, а когда структура?

Структуру лучше использовать, если:

  • не нужно наследование;
  • инкапсулируется малый набор данных;
  • значение копируется, а не передается по ссылке;
  • можно обойтись структурой.

18. Что такое Generics?

Подобный вопрос на собеседовании задают на позицию Junior+.

Generics или дженерики — универсальные шаблоны, которые разрешают создавать универсальные функции и типы. Работают с каждым типом в соответствии с требованиями, которые определяет разработчик.

Главная особенность — пишется один код, который не дублируется для использования с другими типами. Вероятнее, каждый читатель уже использовал дженерики, даже если этого и не знал: коллекции в Swift, например, Array, Set и Dictionary — универсальные шаблоны. Вы ведь можете создать массив с типом String или Int.

Говоря о дженериках, в пример приводят функцию, которая меняет значения двух переменных местами. Поддержим традицию и рассмотрим аналогичный пример:

func swapValues(_ a: inout Int, _ b: inout Int) {
    let tmpA = a
    a = b
    b = tmpA
}

Функция swapValues, используя сквозные параметры, меняет местами значения переменных a и b. Давайте запустим код:

var aInt = 5
var bInt = 10
swapValues(&aInt, &bInt)
print("aInt: \(aInt), bInt: \(bInt)") // aInt: 10, bInt: 5

Как видим, значения переменных поменялись местами. Но что если нужно сделать то же самое, но только для типа Double? Напишем вторую функцию!

func swapIntValues(_ a: inout Int, _ b: inout Int) {
    let tmpA = a
    a = b
    b = tmpA
}

func swapDoubleValues(_ a: inout Double, _ b: inout Double) {
    let tmpA = a
    a = b
    b = tmpA
}

Эти функции будут работать так, как и задумали, но интересен вопрос: как сделать подобный функционал еще для нескольких типов? Не дублировать ведь код снова и снова! Используем универсальный шаблон при написании нашей новой функции:

func swapAnyValues<T>(_ a: inout T, _ b: inout T) {
    let tmpA = a
    a = b
    b = tmpA
}

var aDouble = 5.0
var bDouble = 10.0
swapAnyValues(&aDouble, &bDouble)
print("aDouble: \(aDouble), bDouble: \(bDouble)") // aDouble: 10.0, bDouble: 5.0

var aInt = 5
var bInt = 10
swapAnyValues(&aInt, &bInt)
print("aInt: \(aInt), bInt: \(bInt)") // aInt: 10, bInt: 5

Как видим, функция swapAnyValues теперь используется как с Int, так и с Double или String значениями.

19. Что такое многопоточность и какие инструменты вы знаете для работы с многопоточностью?

На собеседовании ожидают услышать, следующее:

  • кандидат понимает, зачем необходима многопоточность в iOS приложениях;
  • кандидат владеет опытом работы или знаком с некоторыми инструментами работы с многопоточностью.

Во-первых, расскажите, что приложение работает в main потоке и как только происходит ресурсозатратный процесс, приложение будет работать медленнее, так как каждое действие будет происходить на главном потоке. Здесь приходит на помощь многопоточность: «дорогие» задачи посылаются в параллельную очередь, и тем самым главный поток разгружается.

Часто, при работе с многопоточностью используется Grand Central Dispatch (GCD) или Operation. На этом этапе важно понимать, чем отличается синхронная очередь от асинхронной и чем эти два инструмента отличаются. Об этом написано в статье: NSOperation или Grand Central Dispatch.

Во-вторых, говоря о многопоточности, расскажите о возможных проблемах при работе с потоками. Об этом написано в статье: Проблемы многопоточности в Swift.

В-третьих, расскажите об инструментах:

20. Что такое Autolayout?

Autolayout помогает создавать динамические пользовательские интерфейсы, масштабируемые и адаптированные к каждому размеру и ориентации устройств. Autolayout вычисляет размер и положение view в иерархии view на основе ограничений (constraints).

Дополнительное чтение: Understanding Auto Layout

Куда пойти дальше:

Выразить благодарность или найти уникальный материал вы можете в boosty.

Подписывайтесь на мой Telegram-канал iOS Interview Channel, чтобы не пропустить новый материал.


Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *