50 вопросов и ответов Swift Interview в 2022 году

Вот список из 50 вопросов и ответов для интервью Swift. Эти вопросы касаются программирования на Swift и разработки приложений для iOS. Вам нужно знать ответы на эти вопросы, прежде чем вы получите шанс продемонстрировать свои навыки перед интервьюером.

Кроме того, не стесняйтесь использовать эти вопросы для интервью Swift, чтобы потренироваться перед экзаменом.

Эти вопросы интервью не в определенном порядке.

1. Что вызывает ошибку в этом фрагменте кода? Как вы могли это исправить?

let n1: Int = 1
let n2: Float = 2.0
let n3: Double = 3.34

var result = n1 + n2 + n3

Отвечать:

В Swift неявное приведение типов между двумя типами данных невозможно.

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

Чтобы исправить это, вам нужно преобразовать каждое значение в один и тот же тип данных. Например:

var result = Double(n1) + Double(n2) + n3

2. Каково значение переменной Len? Почему?

var arr1 = [1, 2, 3]
var arr2 = arr1

arr2.append(4)

var len = arr1.count

Отвечать:

Значение len равно 3, т. е. количество элементов в arr1 равно 3. Это связано с тем, что присвоение arr1 arr2 фактически означает, что копия arr1 присваивается arr2, поэтому arr1 не затрагивается.

В Swift все основные типы данных (логические значения, целые числа и т. д.), перечисления и структуры по своей природе являются типами значений.

Массивы также являются типами значений. При перемещении типа значения в Swift вы, по сути, работаете с его копией. Например, при передаче типа значения в качестве аргумента функции создается его копия, поэтому все, что делает функция, не отражается в исходном значении.

3. В чем проблема и как ее исправить?

Рассмотрим этот фрагмент кода, который пытается получить цвет темы из локального хранилища устройства iOS:

var color = UserDefaults.standard.string(forKey: "themeColor")!
print(color)

Сможете ли вы найти ошибку и исправить ее?

Отвечать:

Первая строка извлекает цвет темы из пользовательских значений по умолчанию. Однако этот метод возвращает необязательное значение (поскольку тема может не быть определена). Если ключ не найден, возвращается ноль, приведенный выше код вылетает с ошибкой:

фатальная ошибка: неожиданно найдено nil при развертывании необязательного значения

Это происходит, когда первая строка использует ! чтобы принудительно развернуть необязательный параметр, который теперь равен нулю. Принудительное развертывание следует использовать только тогда, когда вы на 100 % уверены, что значение не равно нулю.

Чтобы исправить это, вы можете использовать необязательную привязку, чтобы проверить, найдено ли значение для ключа:

if let color = defaults.stringForKey("themeColor") {
    print(color)
}

4. Какие потенциальные улучшения вы видите?

Вы просматриваете запрос на включение и сталкиваетесь с этим методом:

func turnTo(direction: String){
    if direction == "North" {
        northAction()
    } else if direction == "East" {
        eastAction()
    } else if direction == "South" {
        southAction()
    } else if direction == "West" {
        westAction()
    } else {
        print("No valid direction specified")
    }
}

Какие улучшения вы можете предложить разработчику кода?

Отвечать:

Несмотря на то, что этот код может работать, необходимо учитывать две вещи.

Использование жестко закодированных строк, таких как (например, «West»), — плохая идея. Что делать, если кто-то ошибся в написании? Чтобы устранить эту проблему, следует отказаться от жестко запрограммированных строк и вместо этого создать перечисление.

Кроме того, как насчет использования оператора switch вместо длинного оператора if-else?

Благодаря этим улучшениям код становится более безопасным и читабельным:

enum Direction {
    case North
    case East
    case South
    case West
}
func turnTo(direction: Direction){
    switch direction {
    case .North: northAction()
    case .East: eastAction()
    case .South: southAction()
    case .West: westAction()
    default:
        print("No valid direction specified")
    }
}

5. Что такое перечисления в Swift?

Перечисление — это группа связанных значений.

Перечисления позволяют писать типобезопасный код.

enum Direction {
    case North
    case East
    case South
    case West
}

Теперь в вашем коде вы можете вызвать Direction.North, например, вместо использования мистической строки «North» (которая может быть легко написана с ошибкой и вызвать раздражающие ошибки).

Вы можете найти больше информации о перечислениях в Swift в этой статье.

6. Что такое необязательное в Swift? Как вы можете создать его?

Необязательный тип — это тип, который может хранить либо значение, либо nil. Вы можете создать необязательный параметр, добавив вопросительный знак? после любого типа:

var number: Int? = 10

7. Что такое typealias в Swift? Как его создать?

Typealias, как следует из названия, является псевдонимом для существующего типа данных.

Вы можете создать его следующим образом:

typealias Weight = Float

Теперь вы можете использовать Weight вместо Float:

let mass1: Weight = 150.0
let mass2: Weight = 220.0

let total: Weight = mass1 + mass2

8. Назовите некоторые преимущества использования Swift

Назвать несколько:

  • Swift — типобезопасный язык
  • Имеет поддержку замыканий
  • Он имеет необязательную поддержку типов
  • Он имеет встроенную обработку ошибок
  • Он имеет поддержку сопоставления с образцом

9. Назовите 5 операторов передачи управления и опишите, как их использовать.

Пять операторов передачи управления:

  • Break
  • Continue
  • Fallthrough
  • Throw
  • Return

Операторы передачи управления изменяют порядок выполнения вашего кода.

Например, вы можете использовать оператор передачи управления break, чтобы завершить выполнение цикла for, если считаете, что продолжать цикл не нужно:

for choice in choices:
    if isCorrect(choice):
        print("Correct choice found!")
        break

10. Предложите небольшой рефакторинг этого кода

if age < 18 {
    driveCar()
} else {
    doNotDrive()
}

Это выражение чистое и работает хорошо, но не могли бы вы предложить небольшое улучшение рефакторинга, чтобы сделать его еще лучше?

Отвечать:

Вы можете использовать тернарный условный оператор, чтобы преобразовать это выражение в однострочное, что в этом случае не ухудшает читабельность, а улучшает ее.

age < 18 ? driveCar() : doNotDrive()

11. Как улучшить читаемость кода

В нашей компании 20 разработчиков и 20 уникальных стилей кодирования. Как мы можем применить некоторые общие стили кодирования/лучшие практики?

Отвечать:

Начните использовать линтер, например Swiftlint. Линтер — это простой в настройке инструмент, который проверяет и исправляет ваши ошибки и применяет лучшие практики и соглашения от вашего имени.

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

12. Что такое обработчик завершения в Swift?

Обработчики завершения — это замыкания в действии.

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

Здесь используются обработчики завершения. Обработчик завершения — это замыкание, которое «перезванивает» сразу после завершения трудоемкого процесса.

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

13. Как протестировать приложение без физического устройства

Если у вас нет устройства iOS, вы можете использовать симуляторы устройств Apple iOS для тестирования своих приложений на Mac.

14. Что делает init() в Swift?

Метод init() используется для инициализации экземпляра.

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

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

15. Let vs Var в Swift?

В Swift вы можете использовать let для создания константы (значение, которое нельзя изменить) и var для создания переменной (значение, которое можно изменить позже).

Некоторые дополнительные подробности вы можете найти в этой статье.

16. Что такое PLIST?

PLIST, или список свойств, — это словарь пар ключ-значение, который используется для хранения данных в файловой системе вашего проекта. Например, info.plist.

17. Что такое протоколы в Swift? Приведите пример

Протокол — это контракт кода. Он действует как план для свойств, методов и т. д. Он описывает, как должен вести себя тип, соответствующий ему.

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

Вот пример протокола, который описывает животное:

protocol Animal {
    var name: String { get set }
    var color: String { get set }
    func makeSound()
}

Давайте создадим классы Cat и Dog, соответствующие протоколу Animal. Таким образом, требуется, чтобы они обе реализовали поведение, описанное в протоколе Animal, то есть имя переменных, цвет и метод makeSound():

class Cat: Animal {
    var name = "Luna"
    var color = "gray"
    func makeSound() {
        print("Meow!")
    }
}
class Dog: Animal {
    var name = "Charlie"
    var color = "black"
    func makeSound() {
        print("Woof!")
    }
}

18. Что такое оператор двойного вопросительного знака?

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

Объединение Nil можно использовать как сокращение для проверки того, является ли необязательное значение nil. Например, вы можете заменить это:

var name: String?

if name != nil {
    print(name)
} else {
    print("N/A")
}

С этим:

print(name ?? "N/A")

19. Что такое защитное заявление?

Защитный оператор используется для передачи управления программой за пределы области видимости. Оператор Guard аналогичен оператору if, но выполняется только при невыполнении некоторых условий.

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

func myFun() {
    guard false else {
         print("This block is run")
         return
    }
    print("This is never run")
}

myFun()

вывод:

This block is run

Узнайте больше о ключевом слове guard, прочитав эту статью.

20. Каковы три основных типа коллекций в Swift?

  • Массивы. Массив — это упорядоченный набор значений.
  • Наборы: набор представляет собой неупорядоченный набор значений.
  • Словари. Словарь представляет собой неупорядоченный набор пар ключ-значение.

21. Что такое defer в Swift?

Вы можете использовать метод defer для выполнения кода перед выходом из области видимости. В качестве примера напечатаем что-нибудь прямо перед завершением выполнения функции:

func printStuff() {
    defer { 
        print("I some printed numbers and now I exit the scope")
    }
    print("4")
}
printStuff()
// Output:
// 4
// I printed numbers and now I exit the scope

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

22. Можно ли поменять местами две переменные без третьей вспомогательной переменной?

Это классический вопрос интервью Swift.

Да, это возможно.

С помощью деструктуризации кортежей вы можете решить проблему следующим образом:

var a = 1
var b = 2

(a, b) = (b, a)

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

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

24. Что такое необязательная цепочка?

Необязательная цепочка означает, что вы можете безопасно вызывать свойство чего-то, что может быть нулевым.

Необязательная цепочка работает, как следует из названия, путем объединения одного или нескольких необязательных значений с оператором вопросительного знака ?, например так:

something?.someValue?.someMethod()

Если в какой-либо точке приведенной выше цепочки встречается nil, приложение не аварийно завершает работу — вместо этого возвращается nil.

25. Что такое необязательная привязка?

Необязательная привязка проверяет, содержит ли необязательный параметр значение или нет. Если у option есть значение, опциональная привязка делает это значение временно доступным:

Например, следующий код проверяет, является ли имя нулевым или нет. Если это не так, создается временная константа realName и ей присваивается имя значения.

var name: String? = "Charles"

if let realName = name {
    print (realName)
}

вывод:

Charles

26. Объясните архитектуру MVC

MVC (Model-View-Controller) — это программная архитектура для разработки приложений iOS. Это одна из фундаментальных концепций разработки приложений для iOS.

Несколько фреймворков iOS используют MVC.

Идея MVC заключается в передаче данных из одного места в другое. Это означает, что любой объект попадает в одну из этих трех категорий:

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

27. Что такое параметр In-Out в Swift?

Параметр inout позволяет изменить значение параметра внутри функции.

Чтобы сделать параметр входным, используйте ключевое слово inout перед типом параметра.

Чтобы передать переменную как ввод-вывод, используйте & перед ее именем.


func change(_ number: inout Int){
    number = 2
}
var number = 1
change(&number)
print(number)
// Output: 
// 2

Узнайте больше о входных параметрах здесь.

28. Что такое кортеж? Продемонстрируйте, как работать с кортежами

Кортеж — это значение, которое можно использовать для объединения нескольких значений, например, в виде пары.

Значения кортежа не обязательно должны быть одного типа.

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

Например:

var coordinates3D = (1.0, 2.0, 5.0)

Чтобы получить доступ к значению внутри кортежа, используйте запись через точку и индекс:

let xPos = coordinates3D.0

Также можно создавать кортежи, чтобы каждое значение имело имя:

var coordinates3D = (x: 1.0, y: 2.0, z: 5.0)

В этом случае вы можете получить доступ к определенному значению кортежа по его имени:

let xPos = coordinates3D.x

29. Что такое быстрые сообщения?

Swift Messages — это библиотека, используемая для отображения сообщений в виде строки состояния вверху или внизу экрана устройства iOS.

30. Можно ли присвоить параметру функции значение по умолчанию?

Для параметра можно указать значение по умолчанию:

func eat(food: String = "spaghetti") {
    print("Yum! I ate some good \(food).")
}

31. Что такое дженерики? Приведите пример использования дженериков

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

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

Здесь вы можете использовать дженерики.

Например, вы можете создать общий тип для параметров (для представления любого типа), используя букву, например. Т нравится это:

struct Vec3D<T> {
    let x, y, z: T
    init(x: T, y: T, z: T) {
        self.x = x
        self.y = y
        self.z = z
    }
}
let intVector = Vec3D(x: 1, y: 2, z: 5)
let floatVector = Vec3D(x: 1.0, y: 2.0, z: 5.0

32. Как называется свойство в фунтах ?

class Weight {
    var kilograms: Float = 0.0
    var pounds: Float {
        get {
            return (kilograms * 2.205)
        }
        set(newWeight) {
            kilograms = newWeight / 2.205
        }
    }
}
let weight = Weight()
weight.kilograms = 100
print(weight.pounds) // prints '220.5'
weight.pounds = 315
print(weight.kilograms) // prints '142.85715'

Отвечать:

Свойство фунтов также известно как вычисляемое свойство.

В Swift вычисляемые свойства не сохраняются в объекте. Вычисляемое свойство означает, что его значение вычисляется «по запросу» только при попытке доступа к нему. Вы можете создавать вычисляемые свойства, используя методы get и (необязательно) set.

  • Метод get выполняет вычисления «по запросу», когда вызывается weight.pounds.
  • Метод set обновляет килограммы при обновлении фунтов. (Обратите внимание, что метод set является необязательным, и вам не нужен такой метод для создания вычисляемого свойства.)

33. В чем разница между операторами == и ===?

  • == — оператор равенства.
  • === — это оператор идентификации.
  • === — это оператор идентификации.

Оператор равенства == используется для проверки равенства двух типов Equatable:

"Hello" == "Hello"
10.0 == 5.0 + 5.0

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

class Fruit {
    var name = "Banana"
}

let fruit1 = Fruit()
let fruit2 = fruit1 // fruit2 now points to same address as fruit

1fruit1 === fruit2 // true

Узнайте больше о том, в чем разница между == и === здесь.

34. Что такое расширения?

В Swift вы можете использовать расширения для добавления функциональности к существующему типу.

В Swift вы можете создать расширение, используя ключевое слово extension:

extension SomeExistingType {
    // add new functionality here
}

35. Что такое вложенная функция?

Вложенная функция — это комбинация функции внутри функции:

func outer() {
    func inner() {
       // Do something here
    }
}

36. Как создать базовый класс в Swift?

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

37. Что такое развертывание силы? Когда вы должны использовать это?

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

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

38. Перечислите преимущества функций высшего порядка.

  • Они обеспечивают гибкость.
  • Они полезны в асинхронных вызовах, где нельзя использовать обычные функции.
  • Иногда они улучшают качество кода и делают его короче и лаконичнее.

39. Fileprivate или Private?

  • Свойство fileprivate можно прочитать в любом месте того же файла Swift, но не за его пределами.
  • Private свойство можно прочитать только внутри того типа, в котором оно было объявлено (а также в расширениях этого типа в том же файле).

Узнайте больше о private и fileprivate здесь.

40. Какие функции есть в Swift?

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

Обычно функции принимают некоторые значения, с которыми они могут работать.

41. Nil против None в Swift?

Между ними нет никакой разницы:

nil == .none // returns true

Единственная «разница» в том, что использование nil более распространено, чем использование none.

42. Что такое словарь в Swift?

Словарь — это базовый тип коллекции в Swift. Его можно использовать для хранения пар ключ-значение.

Вы можете легко получить доступ к значению, зная ключ:

let dict = ["a": 1, "b": 2]
let valueOfA = dict["a"]

43. Что делает мутирующее ключевое слово?

Вы можете использовать ключевое слово mutating, чтобы разрешить изменение свойств структуры в методе, пометив этот конкретный метод как мутирующий.

Например:

struct Fruit {
    var type: String
    mutating func convertToBanana() {
        self.type = "Banana"
    }
}
var fruit = Fruit(type: "Apple")
fruit.convertToBanana()
print(fruit.type) // prints "Banana"

По умолчанию это невозможно для типов значений (структур и перечислений), но возможно для ссылочных типов (классов).

44. Можете ли вы исправить проблему в этом коде?

Этот код ниже вызывает ошибку компилятора. Что не так? Как это исправить?

struct Apple {}
func pick(apple: Apple?) {
  guard let apple = apple else {
    print("No apple found!")
  }   
  print(apple)
}

Отвечать:

Для блока else оператора Guard требуется путь выхода.

Вы можете, например, использовать return, чтобы предоставить ему один:

struct Apple {}
func pick(apple: Apple?) {
  guard let apple = apple else {
    print("No apple found!")
    return
  }   
  print(apple)
}

45. Что такое деинициализатор? Как его создать?

Деинициализатор запускается перед освобождением экземпляра класса.

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

Этот метод полезен только в том случае, если вам нужно выполнить некоторую уборку перед освобождением экземпляра класса. В большинстве случаев достаточно позволить Swift сделать это автоматически от вашего имени.

Вот пример деинициализатора, который устанавливает число обратно в 0, когда экземпляр Example освобождается.

var number = 15

class Example {
    init() {
       number *= 10
    }
    
    deinit {
        number = 0
    }
}

46. В чем разница между функциями и методами?

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

47. Как запретить наследование класса?

Делая класс конечным классом, используя ключевое слово final. Например:

final class Animal {
let name = "I'm a furry animal"
}

Узнайте больше о выпускных классах и их преимуществах здесь.

48. Что такое ленивые переменные? Когда вы должны использовать?

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

Например:

lazy var tallest: Person? = {
    return people.max(by: { $0.height < $1.height })
}()

Чтобы узнать больше о ленивых переменных, ознакомьтесь с этой статьей.

49. Что такое автозакрытие в Swift? Как и когда вы должны использовать?

Автозакрытие оборачивает аргумент функции в замыкание.

Когда вызывается автозамыкание, оно возвращает значение выражения, завернутого внутрь.

Автозамыкание — не что иное, как синтаксическое удобство для написания более чистого кода.

Иногда синтаксически удобно использовать автозамыкания при работе с функцией, которая принимает аргумент замыкания.

Это связано с тем, что автозакрытие позволяет не использовать фигурные скобки {}.

Это может сделать код более читаемым.

Однако имейте в виду, что Apple говорит об использовании автозамыканий:

Обычно вызывают функции, которые принимают автозамыкания, но редко реализуют такие функции.

Вот пример автоматического закрытия, упрощающего код. Первый фрагмент использует обычное закрытие, а второй — автоматическое закрытие. Посмотрите, как вызов функции I_will становится более читабельным во втором примере:

func I_will(_ perform_action: () -> Void) {
    perform_action()
}
I_will({
    print("Hello, world!")
})
func I_will(_ perform_action: @autoclosure () -> Void) {
    perform_action()
}
I_will(print("Hello, world"))

Как видите, вызов функции I_will больше не требует использования фигурных скобок.

50. Чего не хватает в этом фрагменте кода?

enum Example {
  case something(Int, Example)
}

Отвечать:

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

enum Example {
  indirect case something(Int, Example)
}

Вывод

Это много вопросов для интервью Swift.

Спасибо за чтение. Надеюсь, они пригодятся вам и помогут найти работу вашей мечты!