- API fetch
- Array
- Async await
- Class
- Closures
- Computed property
- Concurrency
- Constants
- Data types
- Defer statement
- Dictionary
- Enum
- Escaping closure
- Extension
- For loop
- forEach
- Function
- Generics
- Guard statement
- if let statement
- Inheritance
- inout
- Lazy var
- Operator
- Optionals
- Property observers
- Property wrapper
- Protocol
- String formatting
- String interpolation
- Struct
- Switch statement
- Try catch
- Tuple
- Variables
- While loop
SWIFT
Swift Defer Statement: Syntax, Usage, and Practical Examples
The defer
statement in Swift lets you schedule code to run just before the current scope exits—no matter how that exit occurs. This makes it ideal for managing cleanup tasks like closing files, releasing resources, or resetting state.
By centralizing finalization logic, defer
enhances readability and ensures consistent execution, even when early returns or errors occur. It’s a simple but powerful tool for writing safer, more maintainable code.
What Does defer
Do?
A defer
block delays its execution until the surrounding scope finishes. This could be a function, loop, or conditional. Regardless of how that scope exits—via return
, break
, continue
, or throw
—the deferred code will run.
Example:
func example() {
defer {
print("This runs at the end")
}
print("This runs first")
}
Output:
This runs first
This runs at the end
The deferred block runs after the rest of the function body but before the function returns.
Syntax Overview
The structure is straightforward:
defer {
// Code to run before exiting scope
}
You can include multiple defer
blocks. They’ll execute in reverse order—last-in, first-out (LIFO).
Common Use: Resource Cleanup
A typical use case is cleanup—ensuring that resources are released, even if the function exits early:
func readFile() {
let file = openFile("data.txt")
defer {
closeFile(file)
}
print("Reading from file")
// Early return or error won’t skip closing the file
}
The cleanup logic is kept close to where the resource is used, improving safety and clarity.
Multiple Defer Blocks
Each block is pushed onto a stack and executed in reverse order:
func demo() {
defer {
print("Last defer")
}
defer {
print("First defer")
}
print("Function body")
}
Output:
Function body
First defer
Last defer
This behavior makes it easy to layer cleanup logic without interfering with control flow.
Error Handling with Defer
Deferred blocks are especially helpful in throwing functions. They ensure critical actions—like logging, resetting state, or closing resources—always happen before the error propagates.
func loadData() throws {
print("Opening database")
defer {
print("Closing database")
}
throw NSError(domain: "", code: 1, userInfo: nil)
}
Output:
Opening database
Closing database
Even though an error is thrown, the deferred code still executes.
Working with Guard Statements
Combining guard
with defer
allows early exits while keeping finalization logic centralized:
func process(input: String?) {
defer {
print("Finished processing")
}
guard let value = input else {
print("Input is nil")
return
}
print("Processing \(value)")
}
Output for nil input:
Input is nil
Finished processing
Output for valid input:
Processing Hello
Finished processing
No matter the path taken, the cleanup code runs.
Practical Use Cases
Typical scenarios where defer
shines:
- Releasing file handles
- Closing database connections or network sockets
- Unlocking mutexes or semaphores
- Resetting temporary states
- Logging function exits for debugging
- Rolling back partial work in transactional code
The key advantage is having cleanup logic in one predictable place.
Comparison to finally
in Other Languages
In other languages, cleanup logic is typically handled using finally
blocks:
- Java:
try { ... } finally { ... }
- Python:
try: ... finally: ...
Swift’s defer
plays a similar role but doesn’t require try
. It can be placed anywhere in a scope and is always executed when that scope ends.
Scoped Behavior in Loops
Each scope maintains its own defer stack. This means defer
blocks inside loops or conditional branches only run when that specific scope exits.
for i in 1...3 {
defer {
print("Deferred for \(i)")
}
print("Loop iteration \(i)")
}
Output:
pgsql
CopyEdit
Loop iteration 1
Deferred for 1
Loop iteration 2
Deferred for 2
Loop iteration 3
Deferred for 3
Each iteration handles its own deferred logic independently.
Performance Considerations
Using defer
introduces a small overhead because Swift must track the deferred blocks. For most use cases, this is negligible. However, in tight loops or performance-critical sections, consider whether the clarity is worth the cost.
In cleanup-heavy code or error-prone logic, the safety and maintainability usually outweigh performance concerns.
Common Mistakes
To get the most out of defer
, avoid these pitfalls:
- Skipping an exit path: The deferred block won’t run if the scope never exits.
- Overusing multiple blocks: Too many defers in one scope can become hard to follow—use comments or group them logically.
- Putting heavy logic inside defer: Keep deferred blocks concise and focused on cleanup or finalization.
- Misunderstanding execution order: Remember—defer blocks are executed in reverse.
Clear naming and structure can help avoid confusion when multiple blocks are involved.
Summary
The defer
statement is a reliable and elegant way to guarantee that cleanup code always runs before a scope ends. It reduces duplication, improves clarity, and prevents subtle bugs—especially in code with multiple exit paths or error handling.
By placing deferred actions where resources are acquired, you ensure consistent cleanup without cluttering your logic. Whether you're managing files, network sessions, or UI state, defer
helps you write safer, cleaner Swift code.
Sign up or download Mimo from the App Store or Google Play to enhance your programming skills and prepare for a career in tech.