What is it?
defer
is a keyword in Swift.
A defer statement
looks like this:
defer { ... }
Inside of the defer statement
, within the brackets, you have executable code.
defer { print("Executable code") }
This executable code can be one or more code statements
. In the example below the print()
is a code statement
and we have two of them:
defer {
print("Code statement 1")
print("Code statement 2")
}
You can have one or more defer statements
in a scope. In this example, the scope is the scope of someFunction()
:
func someFunction() {
defer { print("Defer statement 1") }
defer { print("Defer statement 2") }
}
What does it do?
A defer statement
isolates a piece of code you want to run right before you leave the scope.
For example, I have a function that will print out a story:
func tellAStory() {
print("Once upon a time...")
print("There was a girl named Robin")
print("who wrote a blog about defer statements.")
print("Everybody loved her blog, and learned a lot.")
print("Robin was very happy.")
}
// Command:
tellAStory()
// Output:
Once upon a time...
There was a girl named Robin
who wrote a blog about defer statements.
Everybody loved her blog, and learned a lot.
Robin was very happy.
I want all of my stories to end with the phrase “the end,” so I add a defer statement
that prints “the end”:
func tellAStory() {
defer { print("The end.") }
print("Once upon a time...")
print("There was a girl named Robin")
print("who wrote a blog about defer statements.")
print("Everybody loved her blog, and learned a lot.")
print("Robin was very happy.")
}
// Command:
tellAStory()
// Output:
Once upon a time...
There was a girl named Robin
who wrote a blog about defer statements.
Everybody loved her blog, and learned a lot.
Robin was very happy.
The end.
Notice how I added the defer statement
at the top.
Notice in the output how “the end” appears as the last line of the story.
This is the magic of the defer statement
: it will always be run right before we leave the current scope, no matter what line I place it on.
It “defers” or “postpones” the execution of some code until the very end.
This means that these three versions of tellAStory()
will all have exactly the same output:
func tellAStory() {
defer { print("The end.") }
print("Once upon a time...")
print("There was a girl named Robin")
print("who wrote a blog about defer statements.")
print("Everybody loved her blog, and learned a lot.")
print("Robin was very happy.")
}
func tellAStory() {
print("Once upon a time...")
print("There was a girl named Robin")
defer { print("The end.") }
print("who wrote a blog about defer statements.")
print("Everybody loved her blog, and learned a lot.")
print("Robin was very happy.")
}
func tellAStory() {
print("Once upon a time...")
print("There was a girl named Robin")
print("who wrote a blog about defer statements.")
print("Everybody loved her blog, and learned a lot.")
print("Robin was very happy.")
defer { print("The end.") }
}
// Command:
tellAStory()
// Output:
Once upon a time...
There was a girl named Robin
who wrote a blog about defer statements.
Everybody loved her blog, and learned a lot.
Robin was very happy.
The end.
Anything else I should know?
Yes! Things get weird when you have multiple defer statements
within the same scope.
Let’s add a second defer statement
to our story function:
func tellAStory() {
defer { print("The end.") }
defer { print("Everyone lived happily ever after.") }
print("Once upon a time...")
print("There was a girl named Robin")
print("who wrote a blog about defer statements.")
print("Everybody loved her blog, and learned a lot.")
print("Robin was very happy.")
}
You expect the output to be:
// Expected Output
Once upon a time...
There was a girl named Robin
who wrote a blog about defer statements.
Everybody loved her blog, and learned a lot.
The end.
Everyone lived happily ever after.
But actually, the output looks like this:
// Actual Output
Once upon a time...
There was a girl named Robin
who wrote a blog about defer statements.
Everybody loved her blog, and learned a lot.
Everyone lived happily ever after.
The end.
When you have multiple defer statements
within the same scope, you start at the bottom and move toward the top.
func someFunction() {
defer { print("Third") }
defer { print("Second") }
defer { print("First") }
}
// Command:
someFunction()
// Output:
First
Second
Third
When should I use it?
defer statements
are rare, but you may see them in code that works with files.
Because it’s important to close a file after you modify it, you can use a defer statement
in your scope that will make sure you never forget to close the file.
func someFunction() {
defer { closeFile() }
// open file
// modify the file
}
Best Practices
It’s a good practice to avoid using defer statements
unless you have a very good reason (like the file example above).
Because defer statements
have an unnatural execution order, they can make your code harder to read and understand. We expect our code to be executed from top to bottom within a scope, and the defer
behavior goes against that expectation.
Conclusion
The defer statement
is an interesting piece of Swift that can “defer” or “postpone” a bit of code so it is run right before you leave the current scope. While useful when working with files, it should be used with caution to avoid making your code hard to understand.