ПрограммированиеПрограммист Swift

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

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

Ответ.

Оператор defer откладывает выполнение кода до выхода из текущей области видимости, обычно из функции. Удобен для очистки ресурсов, закрытия файлов и освобождения памяти — так называемый автоматизированный cleanup.

Особенности:

  • Блоки defer выполняются в обратном порядке их объявления (LIFO).
  • Вызов происходит всегда — и на нормальном выходе из функции, и при выбрасывании ошибки.
  • Позволяет избежать дублирования кода очистки и поддерживает чистую архитектуру функций.

Пример:

func processFile() { let file = openFile() defer { closeFile(file) } // Работа с файлом // file будет закрыт даже в случае throw или return }

Нетипичное применение: частичная имитация конструкций типа 'finally', группировка кода для логирования и отслеживания времён работы.

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

Выполнится ли defer, если приложение завершает работу в процессе исполнения функции?

— Нет. Defer выполняется только при нормальном завершении области видимости (return, throw или нормальный выход). Если приложение аварийно завершает работу (например, из-за сигнала SIGKILL или fatalError), блоки defer не успеют выполниться.

Пример:

func foo() { defer { print("Clean up") } fatalError("Crash!") // defer не выполнится }

Примеры реальных ошибок из-за незнания тонкостей темы.


История

Функция, работавшая с сокетами, не очищала соединение в случае throw. Данные оставались висеть, соединения не освобождались. После ввода defer всё работало корректно: ресурсы закрывались всегда.


История

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


История

В функции объявили несколько defer, полагая, что они выполнятся в порядке объявления. В результате ресурсы закрывались в неверном порядке (например, сначала закрылся file descriptor, а потом к нему пытались обращаться). Решение: помнить о LIFO-порядке выполнения defer-блоков.