Введение: Почему Swift — ключ к успешному собеседованию
Собеседование по Swift — это не просто проверка синтаксиса. Apple-экосистема требует понимания фундаментальных принципов языка, управления памятью и современных фреймворков. В этой статье мы разберем топ вопросов, которые вам обязательно зададут на любом уровне — от Junior до Senior. Вы узнаете не только правильные ответы, но и типичные ловушки, в которые попадают кандидаты.
Статья написана для образовательного портала PythonLib, но адаптирована под Swift. Мы структурировали материал так, чтобы вы могли быстро повторить ключевые темы перед интервью.
1. Основы Swift: Optional, Value vs Reference Types
Что такое Optional и зачем он нужен?
Optional — это перечисление (enum), которое может быть либо .none (nil), либо .some(Wrapped). Это способ Swift безопасно работать с отсутствием значения.
var name: String? = "Alice"
var age: Int? = nil
// Безопасное извлечение
if let unwrappedName = name {
print("Hello, \(unwrappedName)")
} else {
print("Name is nil")
}
Типичная ошибка: Использование force unwrap (!) без проверки. Всегда предпочитайте guard let или if let.
Value Types vs Reference Types
Swift строго разделяет типы:
- Value Types (структуры, перечисления, базовые типы) — копируются при присваивании.
- Reference Types (классы) — передаются по ссылке.
struct Point {
var x: Int
var y: Int
}
class Car {
var model: String
init(model: String) { self.model = model }
}
var point1 = Point(x: 1, y: 2)
var point2 = point1
point2.x = 5 // point1.x останется 1
var car1 = Car(model: "Tesla")
var car2 = car1
car2.model = "BMW" // car1.model тоже станет "BMW"
Вопрос на собеседовании: "Почему Apple рекомендует использовать структуры вместо классов?" Ответ: безопасность потоков, предсказуемость, отсутствие ARC-оверхеда.
2. Memory Management и ARC: Как избежать утечек
Как работает Automatic Reference Counting (ARC)?
ARC автоматически подсчитывает ссылки на объекты классов. Когда счетчик достигает нуля, память освобождается. Проблемы возникают при циклических ссылках.
class Person {
let name: String
var apartment: Apartment?
init(name: String) { self.name = name }
deinit { print("\(name) deallocated") }
}
class Apartment {
let unit: String
weak var tenant: Person? // weak — слабая ссылка
init(unit: String) { self.unit = unit }
deinit { print("Apartment \(unit) deallocated") }
}
var john: Person? = Person(name: "John")
var unit4A: Apartment? = Apartment(unit: "4A")
john?.apartment = unit4A
unit4A?.tenant = john
john = nil
unit4A = nil
// Оба объекта будут освобождены
Ключевые модификаторы:
weak— всегда optional, автоматически становится nil при освобождении.unowned— не optional, предполагает, что объект жив (риск crash).
Типичный вопрос: "В чем разница между weak и unowned?" Ответ: weak безопаснее, unowned удобнее для обязательных зависимостей (например, делегат).
3. Фреймворки: UIKit vs SwiftUI, GCD и Protocols
UIKit: Жизненный цикл ViewController и Delegate Pattern
Понимание viewDidLoad, viewWillAppear, viewDidDisappear — база. Но на собеседовании часто спрашивают про Delegate pattern:
protocol MyDelegate: AnyObject {
func didTapButton()
}
class MyView: UIView {
weak var delegate: MyDelegate?
@objc func buttonTapped() {
delegate?.didTapButton()
}
}
class MyViewController: UIViewController, MyDelegate {
let myView = MyView()
override func viewDidLoad() {
super.viewDidLoad()
myView.delegate = self
}
func didTapButton() {
print("Button was tapped!")
}
}
Важно: Делегат должен быть AnyObject (только классы) и объявлен как weak.
SwiftUI: @State, @Binding и реактивное программирование
SwiftUI меняет парадигму. Основные property wrappers:
@State— для локального состояния внутри view.@Binding— для передачи состояния дочерним view.@ObservedObject/@StateObject— для внешних моделей.
struct CounterView: View {
@State private var count = 0
var body: some View {
VStack {
Text("Count: \(count)")
Button("Increment") {
count += 1
}
}
}
}
Вопрос: "Когда использовать @StateObject, а когда @ObservedObject?" Ответ: @StateObject — когда view создает объект, @ObservedObject — когда получает его извне.
Grand Central Dispatch (GCD) и Concurrency
Понимание очередей — must-have:
DispatchQueue.global(qos: .background).async {
// Тяжелая работа в фоне
let result = performHeavyTask()
DispatchQueue.main.async {
// Обновление UI
self.label.text = result
}
}
Новые подходы: async/await (Swift 5.5+) — более читаемый код:
func fetchData() async throws -> Data {
let url = URL(string: "https://api.example.com")!
let (data, _) = try await URLSession.shared.data(from: url)
return data
}
Типичная ошибка: Забыть переключиться на main queue для UI-обновлений.
4. Продвинутые темы: Generics, Protocols с Associated Types, Result Builders
Generics и Protocol-Oriented Programming
Swift — язык с протокол-ориентированным программированием. Умение писать generic-код — признак Senior-уровня:
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {