Введение: почему Swift кусается, даже если вы его любите
Вы решили стать iOS-разработчиком. Установили Xcode, написали первое «Hello, World!» — и почувствовали себя почти Тимом Куком. А потом началось: Xcode ругается на непонятные nil, код не компилируется, а приложение падает при первом же нажатии кнопки. Знакомо?
Добро пожаловать в мир Swift — языка, который обожают за безопасность, но ненавидят за строгость. В этой статье мы разберём ошибки при изучении Swift, которые делают 90% новичков. Вы узнаете, как их избежать, сэкономить нервы и перестать проклинать Apple. Поехали!
Ошибка №1: Игнорирование Optional и работа с nil «на авось»
Swift — первый язык, где nil — это не просто «пусто», а целая философия. Новички часто пытаются работать с опционалами как в Python или JavaScript: «ну, может быть там что-то есть, а может и нет». И получают краш.
Как это выглядит в коде
// ❌ Типичная ошибка новичка
var userName: String? = nil
print("Привет, \(userName.uppercased())!") // Fatal error: Unexpectedly found nil
Здесь userName — опционал (String?). Вы забыли проверить, есть ли в нём значение, и попытались вызвать метод uppercased() на nil. Приложение падает.
Правильный подход
// ✅ Безопасное извлечение
if let safeName = userName {
print("Привет, \(safeName.uppercased())!")
} else {
print("Имя не задано")
}
// Или с помощью guard
guard let unwrappedName = userName else {
print("Ошибка: имя отсутствует")
return
}
print("Привет, \(unwrappedName.uppercased())!")
Совет: Всегда думайте: «А может ли эта переменная быть nil?». Если да — используйте if let или guard let. Это одна из самых частых ошибок начинающих в Swift, которая убивает время на отладку.
Ошибка №2: Боязнь замыканий (Closures) и использование @escaping
Замыкания в Swift — это как анонимные функции. Новички их боятся, пишут огромные методы вместо компактных замыканий, а потом удивляются, почему код не читается. А когда доходит до асинхронности — начинается ад с @escaping.
Пример страха перед замыканиями
// ❌ Код, который можно сократить в 3 раза
func fetchData(completion: (String) -> Void) {
let result = "Данные загружены"
completion(result)
}
// Вызов
fetchData { data in
print(data)
}
Всё красиво? Да. Но новички пишут так:
// ❌ Ужасный стиль
func fetchData() {
let result = "Данные загружены"
handleResult(result: result)
}
func handleResult(result: String) {
print(result)
}
А когда нужно сделать асинхронный запрос, забывают про @escaping:
// ❌ Ошибка: замыкание не escaping
func loadUser(id: Int, completion: (User) -> Void) {
DispatchQueue.global().async {
let user = User(id: id)
completion(user) // Ошибка компиляции!
}
}
Xcode скажет: «Замыкание должно быть @escaping». Исправляем:
// ✅ Правильно
func loadUser(id: Int, completion: @escaping (User) -> Void) {
DispatchQueue.global().async {
let user = User(id: id)
completion(user)
}
}
Вывод: Не бойтесь замыканий. Они — ваши друзья. А @escaping — просто флаг, который говорит: «Это замыкание будет вызвано позже, сохрани его». Запомните это — и половина ошибок Swift для начинающих исчезнет.
Ошибка №3: Путаница между классами и структурами
В Swift есть классы (ссылочный тип) и структуры (значимый тип). Новички часто используют классы везде, потому что «так привыкли из других языков». А потом удивляются, почему данные меняются в одном месте и ломаются в другом.
Классический баг
// ❌ Класс — ссылочный тип
class User {
var name: String
init(name: String) { self.name = name }
}
let user1 = User(name: "Анна")
let user2 = user1
user2.name = "Мария"
print(user1.name) // Мария! user1 тоже изменился
Для новичка это шок: «Я поменял user2, а user1 тоже изменился!». Всё верно: классы — это ссылки. Если нужно, чтобы копии были независимы — используйте структуры.
// ✅ Структура — значимый тип
struct User {
var name: String
}
var user1 = User(name: "Анна")
var user2 = user1
user2.name = "Мария"
print(user1.name) // Анна! Всё ок
Правило: Используйте struct по умолчанию. Классы — только когда нужно наследование или работа с Objective-C. Это одна из главных типичных ошибок Swift, которая ведёт к трудноуловимым багам.
Ошибка №4: Неправильная работа с памятью и retain cycles
Swift имеет ARC (Automatic Reference Counting), но это не значит, что можно забыть про управление памятью. Новички создают сильные ссылки в замыканиях и получают утечки памяти.
Пример утечки
// ❌ Retain cycle
class ViewController: UIViewController {
var onButtonTap: (() -> Void)?
override func viewDidLoad() {
super.viewDidLoad()
onButtonTap = {
self.view.backgroundColor = .red // Сильная ссылка на self
}
}
}
Замыкание захватывает self сильно, self держит замыкание — цикл замкнут. Объект не удалится из памяти.
Решение
// ✅ Используем weak или unowned
onButtonTap = { [weak self] in
self?.view.backgroundColor = .red
}
// Или unowned, если self точно жив
onButtonTap = { [unowned self] in
self.view.backgroundColor = .red
}
Совет: Всегда используйте [weak self] в замыканиях, которые могут жить дольше, чем объект. Это спасёт от утечек памяти. Особенно важно в изучении Swift для начинающих, когда вы пишете сетевые запросы или анимации.