ПрограммированиеiOS/Swift разработчик

Что такое заявления defer в Swift? Каковы особенности его исполнения, где целесообразно применять и с какими подводными камнями можно столкнуться?

Проходите собеседования с ИИ помощником Hintsage

Ответ.

История вопроса:

Defer был добавлен в Swift (вдохновлённый аналогами в Go, C#) для гарантированной очистки ресурсов даже при возникновении ошибок, выходе из функции или при возврате значений из разных точек функций.

Проблема:

Преждевременное освобождение, забытые чистки ресурсов (например, закрытие файлов, логирование, rollback транзакций). Иногда путают порядок исполнения, ошибочно полагая, что defer выполняется в момент объявления.

Решение:

Defer — это специальный блок, который откладывает выполнение кода до конца текущей области (scope), обычно функции. Все defer выполняются в обратном порядке размещения (LIFO). Это позволяет централизованно управлять очисткой ресурсов, освобождением памяти или откатом транзакций.

Пример кода:

func processFile() { let file = File("/tmp/data.txt") file.open() defer { file.close() print("File closed") } // Работа с файлом print("Reading data…") }

Ключевые особенности:

  • Выполняется всегда при любом выходе из scope, даже при ошибках и раннем return.
  • Несколько defer исполняются в обратном порядке.
  • Может быть применён в любой области, не только в функциях.

Вопросы с подвохом.

Выполняется ли код внутри defer при падении приложения (crash) до выхода из функции?

Нет, код defer исполняется только при корректном выходе из области видимости (scope). Если приложение завершилось аварийно (например, fatal error), defer не выполнится.

Можно ли использовать return внутри defer?

Нет, нельзя. Defer блок не допускает возврат значений или завершение области, только инструкции.

Может ли defer использоваться для модификации переменных, объявленных до defer?

Да, defer захватывает значения с текущего стека в момент исполнения. Можно изменять значения, объявленные до defer, и они сохранятся при выходе из scope.

Пример кода:

func example() -> Int { var result = 0 defer { result = 42 } return result // defer выполнится, результат — 42 }

Типовые ошибки и анти-паттерны

  • Ошибочное ожидание исполнения defer сразу после объявления.
  • Применение defer вне scope, где это не имеет смысла.
  • Оставление тяжёлых операций внутри defer.

Пример из жизни

Негативный кейс

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

Плюсы:

  • Простота реализации

Минусы:

  • Не закрывается файл при ошибках
  • Утечка ресурсов

Позитивный кейс

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

Плюсы:

  • Безопасно, нет утечек
  • Чистый и предсказуемый код

Минусы:

  • Необходима осторожность с изменяемыми ресурсами внутри defer