ПрограммированиеSwift developer

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

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

Ответ.

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

defer был введён в Swift для более удобного управления ресурсами и выполнения операций после завершения блока кода, аналогичного finally в других языках. Такая конструкция помогает явно показывать этапы очистки или завершения работы с ресурсами.

Проблема:

Многие начинающие считают, что defer сразу выполняет вложенный код, не всегда верно понимая момент срабатывания. Также бывают ситуации, когда несколько блоков defer трудно понимать, если не знать правила LIFO (last-in, first-out). Возможны сложности с try/catch и ранним выходом.

Решение:

defer выполняет вложенный код в самом конце scope, перед выходом из текущего блока функции, даже если выход из функции происходит раньше (через return, throw, break и т.д.). Несколько defer выполняются в обратном порядке их появления.

Пример кода:

func readFile() { print("Open file") defer { print("Close file") } print("Read line 1") if Bool.random() { print("Early return!") return } print("Read line 2") } readFile() // В консоли всегда будет: Open file, ..., Close file (в конце)

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

  • defer всегда срабатывает при выходе из scope функции/метода
  • несколько defer работают по принципу стека (LIFO)
  • срабатывает даже при раннем return или error

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

Можно ли использовать defer вне функции?

Нет, defer разрешён только внутри функций, методов, initializers и deinitializers. Его нельзя разместить, например, в глобальном пространстве файла или внутри источника кода вне функций.

Что произойдет с defer, если в блоке будет exception (throw)?

defer всё равно выполнится. Это одно из его преимуществ — ресурс гарантированно освобождается даже в случае ошибки (throw). Так реализуется паттерн безопасного освобождения ресурсов.

В каком порядке выполняются несколько defer?

Они выполняются в обратном порядке (LIFO): тот defer, который был объявлен позднее, выполнится раньше.

Пример кода:

func test() { defer { print("First") } defer { print("Second") } print("Inside") } test() // Выведет: "Inside", "Second", "First"

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

  • Использование defer для кода, в котором могут возникать собственные return/throw, путаница в порядке исполнения
  • Злоупотребление defer, усложнение читаемости функции с множеством блоков
  • Попытка использовать defer вне scope функции

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

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

Функция с несколькими, непоследовательно расположенными defer; забыта очистка некоторых ресурсов, возвращаемся из функции в разных местах. Это приводит к утечкам ресурсов и трудной отладке поведения.

Плюсы: Единообразие кода, "собраны" действия очистки в одном месте.

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

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

Делается один defer для каждог важного этапа, размещён прямо там, где ресурс инициализируется, с комментариями. Проверяется код-ревью.

Плюсы: Гарантированная очистка ресурсов, чёткое понимание порядка действий.

Минусы: Требует дисциплины и внимания при добавлении новых ресурсов и блоков очистки.