defer — это специальная конструкция Swift, позволяющая выполнить определённый блок кода непосредственно перед выходом из области видимости (scope) функции, независимо от того, вышли ли мы из неё через обычный return или по ошибке (throw). Defer удобно использовать для освобождения ресурсов, отмены изменений или финализации операций.
Особенности работы:
Пример:
func testDefer() { print("begin") defer { print("first defer") } defer { print("second defer") } print("end") } // Выведет: // begin // end // second defer // first defer
Граничные случаи:
Будут ли выполены все defer-блоки в функции, если вызвать внутри неё fatalError?
Ответ: Нет, если в функции вызывается fatalError (или похожие неконтролируемые крэши), то все отложенные через defer блоки не выполняются. defer не гарантирует вызова кода в случаях аварийного завершения работы приложения.
Пример:
func foo() { defer { print("Defer 1") } fatalError("Oops") defer { print("Defer 2") } } foo() // ничего не выведет, сработает crash
История
В проекте использовали defer для закрытия файлового дескриптора. При появлении ошибки использовали fatalError вместо корректного throw, что приводило к утечке открытого ресурса, поскольку defer не отрабатывал при crash.
История
В одной функции было несколько defer, некоторые из которых зависят от состояния локальных переменных. Разработчик ожидал, что переменная будет равна специфическому значению, но это значение в defer изменилось другим участком кода, и при выполнении defer использовалось актуальное, а не старое значение, приводя к багу отмены транзакции не по нужному ID.
История
Во вложенном closure написали defer-блок, считая, что он отработает при выходе из внешней функции. В результате этот defer исполнился при выходе из closure, а не всей функции, и ресурс был освобождён слишком рано.