История вопроса:
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 вне функции?
Нет, 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; забыта очистка некоторых ресурсов, возвращаемся из функции в разных местах. Это приводит к утечкам ресурсов и трудной отладке поведения.
Плюсы: Единообразие кода, "собраны" действия очистки в одном месте.
Минусы: Сложно уследить, что выполнится и в каком порядке; возможны утечки при ошибках в логике.
Делается один defer для каждог важного этапа, размещён прямо там, где ресурс инициализируется, с комментариями. Проверяется код-ревью.
Плюсы: Гарантированная очистка ресурсов, чёткое понимание порядка действий.
Минусы: Требует дисциплины и внимания при добавлении новых ресурсов и блоков очистки.