Defer Statement:
Introduced in Swift 2.0, The Swift Defer statement isn't something you'll need to use often.
What is Defer?
A defer statement is used for executing code just before transferring program control outside of the scope that the defer statement appears in. In simple words, defer is an operator used with closure where you include a piece of code that is supposed to be executed at the very end of the current scope.
Syntax
- defer {
- statements
- }
Example
- func f() {
- defer { print("defer statement executed") }
- print("End of function")
- }
- f()
- // Prints "End of function"
- // Prints "defer statement executed"
The above example defer statement was executed after the print statement after the end of function f() 's Scope.
Multiple Defer
One of the most powerful features of defer statement is that you can stack up multiple deferred pieces of work, and Swift will ensure they all get executed.
If you use multiple defer statements in the same scope, they're executed in reverse order of appearance - like a stack, the order is LIFO(Last In First Out) or FILO(First In Last Out).
Example
- func f() {
- defer { print("First defer") }
- defer { print("Second defer") }
- print("End of function")
- }
- f()
- // Prints "End of function"
- // Prints "Second defer"
- // Prints "First defer"
This reverse order is vital, ensuring everything in scope when a deferred block was created will still be in scope when the block is executed.
Not only does this mean you can defer work without having a worry about what if anything was already deferred, but also that Swift safely unwinds its defer stack based on the order you chose.
Defer does not capture value:
If a variable is referenced in the body of a defer statement, its final value is evaluated. That is to say 'Swift defer statement do not capture the current state of a variable, and simply evaluate the value at the time it is run'.
func deferCapture() {
var a = "Hello! "
defer {print (a)}
a += "World"
}
deferCapture()
// Hello! World
So defer does not capture value, it will evaluate at the run time.
Break defer out of scope
The statements in the defer statement can't transfer program control outside of the defer statement. In simple words your defer calls shouldn't try to exit the current scope using something like - return, throw, break, continue, etc.
What is the Use of defer Statement?
I hope you all understand how defer statements work in different conditions, but questions arise what is the use of deferring in our project?
Although the defer keyword was already introduced in Swift 2.0, it's still quite uncommon to use in projects. Its usage can be hard to understand, but using it can improve your code a lot in some places.
Make Sure Resource Cleared
Swift's defer keyword lets us set up some work to be performed when the current scope exits. For Example, you might want to make sure that some temporary resources are cleaned up once a method exits, and defer will make sure that happens no matter how that exit happens.
Example
Here some dummy Swift code that open file, write some data and close the file.
func writeLog() {
let file = openFile()
let hardwareStatus = fetchHardwareStatus()
guard hardwareStatus != "disaster" else { return }
file.write(hardwareStatus)
let softwareStatus = fetchSoftwareStatus()
guard softwareStatus != "disaster" else { return }
file.write(softwareStatus)
let networkStatus = fetchNetworkStatus()
guard neworkStatus != "disaster" else { return }
file.write(networkStatus)
closeFile(file)
}
As you can see, a file is opened, then various types of data are written out before finally the file is closed.
But what happens if any one of those status checks returns "disaster"? Answer: our guard condition will trap the error and exit the method – leaving the file open.
There were two solutions to this, neither of which was nice. The first was to copy and paste the call to closeFile()
so that it is called before any of those returns
. The second was to create a pyramid of doom, with several stacked conditional statements to handle writing.
Defer statement will solve this problem, With that defer
call in place, closeFile()
will be called no matter which of the guards
are triggered, or even if none of them trigger and the method completes normally.
We could write the above code like this
func writeLog() {
let file = openFile()
defer { closeFile(file) }
let hardwareStatus = fetchHardwareStatus()
guard hardwareStatus != "disaster" else { return }
file.write(hardwareStatus)
let softwareStatus = fetchSoftwareStatus()
guard softwareStatus != "disaster" else { return }
file.write(softwareStatus)
let networkStatus = fetchNetworkStatus()
guard neworkStatus != "disaster" else { return }
file.write(networkStatus)
}
Consider using defer whenever an API requires calls to be balanced, such as allocate(capacity:) / deallocate(), wait() / signal(), or open() / close().
Ensuring results
Another usage of statement is by ensuring a result value to be returned in a completion callback. This can be very handy as it's easy to forget to trigger this callback.
func getData(completion: (_ result: [String]) -> Void) {
var result: [String]?
defer {
guard let result = result else {
fatalError("We should always end with a result")
}
completion(result)
}
// Generate the result...
}
The statement makes sure to execute the completion handler at all times and verifies the result value, Whenever the result value is nil, the fatal error is thrown and the application fails.
Note:-In case of Multiple defer, Executing the last defer statement in a given scope first means that statements inside that last defer statement can refer to resources that will be cleaned up by other defer statements.
Conclusion :
Defer is a useful tool in your programming armory. You should be willing to use it, particularly when you think about cleaning up resources after you have completed some particular function.
Comments
Post a Comment