Оператор defer введен в Swift для безопасной и гарантированной очистки ресурсов или выполнения кода в блоке выхода из области видимости, по аналогии с finally/using/RAII в других языках.
Многоступенчатая инициализация, работа с файлами, кросс-сценарии выхода из функции — требуют гарантированного освобождения ресурсов или выполнения логики (закрыть файл, разблокировать mutex, вернуть объект в пул, сбросить временное состояние). До появления defer все приходилось делать вручную в каждом return.
defer гарантирует выполнение его кода при выходе из текущего scope, независимо от того, был ли exit normal или через throw. Можно объявлять несколько defer — они выполнятся в обратном порядке объявления.
Пример освобождения ресурса:
func processFile(path: String) throws { let file = try openFile(path) defer { file.close() } // Гарантированно вызовется даже при ошибках // ... работа с file ... }
Ключевые особенности:
Может ли defer захватить переменные по ссылке (reference) и как это влияет на поведение замыканий?
Да, defer захватывает все использованные переменные в момент вызова, по правилам захвата closure (копия для value-типа, ссылка для reference-типа). Ошибкой станет захват изменяемой внешней value-переменной — она может измениться к моменту выполнения defer.
Как работает вложенный defer внутри циклов и функций?
Если defer объявлен внутри цикла, он выполняется при каждом завершении итерации scope:
for _ in 1...3 { defer { print("End of iteration") } print("Inside") }
Выведет:
Inside
End of iteration
Inside
End of iteration
Inside
End of iteration
Может ли defer не выполниться?
Выполнение кода defer гарантировано только если scope действительно покидается. Но если процесс аварийно завершится (fatalError, crash), или будет вызван exit, defer не отрабатывает. Также defer не работает на уровне методов объектов — только в scope функции/блока.
В коде после открытия файла и работы с ним возврат производился по разным return/throw, забыли в одном месте закрыть файл. В итоге open file handle утек в систему.
Плюсы:
Минусы:
Использование defer для гарантированного unlock mutex, освобождения файлового дескриптора и сброса progress-флага:
func criticalSection() { lock() defer { unlock() } // ... работа ... }
Плюсы:
Минусы: