The defer operator was introduced in Swift for safe and guaranteed cleanup of resources or execution of code in the exit block of a scope, similar to finally/using/RAII in other languages.
Multi-step initialization, file operations, cross-scenario exits from a function — require guaranteed resource release or execution of logic (closing a file, unlocking a mutex, returning an object to a pool, resetting temporary state). Before defer, this had to be done manually in every return.
defer guarantees the execution of its code upon exiting the current scope, regardless of whether the exit is normal or via throw. You can declare multiple defer — they will execute in reverse order of declaration.
Example of resource release:
func processFile(path: String) throws { let file = try openFile(path) defer { file.close() } // Guaranteed to be called even on errors // ... work with file ... }
Key features:
Can defer capture variables by reference and how does it affect closure behavior?
Yes, defer captures all used variables at the time of invocation, according to closure capture rules (copy for value types, reference for reference types). Capturing a mutable external value variable would be an error — it may change by the time defer is executed.
How does nested defer work inside loops and functions?
If defer is declared inside a loop, it executes on every completion of the iteration scope:
for _ in 1...3 { defer { print("End of iteration") } print("Inside") }
Will output:
Inside
End of iteration
Inside
End of iteration
Inside
End of iteration
Can defer not execute?
The execution of defer code is guaranteed only if the scope is truly exited. But if the process crashes (fatalError, crash), or exit is called, defer does not execute. Additionally, defer does not work at the method level of objects — only within the function/block scope.
In the code after opening a file and working with it, returns were made through different return/throw statements, forgetting to close the file in one place. As a result, open file handle leaked into the system.
Pros:
Cons:
Using defer for guaranteed unlocking of the mutex, releasing the file descriptor, and resetting the progress flag:
func criticalSection() { lock() defer { unlock() } // ... work ... }
Pros:
Cons: