Check out the official introduction of Apple
Use the defer statement to execute a series of statements when it is about to leave the current code block. This statement allows you to perform some necessary cleanup work, regardless of how you leave the current block of code - whether it is due to a thrown error, or due to statements such as return, break. For example, you can use the defer statement to ensure that the file descriptor is closed and that the manually allocated memory is freed.
The defer statement delays the execution of the code until the current scope exits. This statement consists of the defer keyword and the statement to be executed delayed. A delayed execution statement cannot contain any control transfer statements, such as break, return statements, or throw an error. Operations that are delayed are executed from behind to front in the order they are declared - that is, the code in the first defer statement is executed last, the code in the second defer statement is executed second to last, and so on. The last statement will be executed first.
Summarize the official introduction of Apple
The defer statement is executed before the scope exits the code block (methods, closures, etc., which can be understood as code wrapped in braces) \color{red}{before scope exit} The scope exits, that is, the code in defer is executed after all other codes that should be executed in the code block have been executed before the code block is executed
A code block allows multiple defers, and the order in which multiple defers are executed is from back to front\color{red}{from back to front}
Some tests and misunderstanding corrections
Test Case 1
func testDefer() { defer { print("Defer content in method") } if true { defer { print("Defer content in if") } print("The last code in if") } print("Code in Method") if true { return } print("The last sentence code before the end of the method") } testDefer()
The above code prints the result:
The last code in if
If defer content
Code in the method
Defer content in the method
In the printing result, the code in the first if and the defer in the method are executed first, and the defer in the method is executed last. From this, it can be seen that other codes that can be executed in the code block are executed first, and the content of the defer can be executed at the end; the scope of the defer cannot be simply regarded as a method, but a code block (some students may have such misunderstandings)
Test Case 2
func testDefer() { print("start") defer { print("Content in defer 1") } defer { print("Content in defer 2") } if true { return } defer { print("Content in defer 3") } print("The last sentence code before the end of the method") } testDefer()
Print results
start
What's in defer 2
Content in defer 1
We can see that the last defer is not executed, so the position of the defer definition is very important. If the code defined by the defer is not executed, the contents in the defer will not be executed before the code block ends.
The execution order of multiple defers is from back to front
Some practical application scenarios
Scenario 1: Some resources need to be released after they are used up. Here is an official case
func processFile(filename: String) throws { if exists(filename) { let file = open(filename) defer { close(file) } while let line = try () { // Process the file. } // close(file) will be called here, that is, the end of the scope. } }
When you start using resources, use defer to release them to avoid forgetting to release resources.
Scene 2: Locking and unlocking, borrowing from kingfisher
let lock = NSLock() func testDefer() { () defer { () } doSomething() } testDefer()
Use defer to unlock immediately after locking to avoid forgetting to unlock
Scenario 3: Handle some repetitive operations before the end of the scope of the code block, such as when requesting network data.
A common way of writing
func loadCityList(_ finish: ((Error?, [String]?) -> ())?) { ().async { // Simulate network requests let data: AnyObject? // Simulate the data returned by the server guard let dict = data as? [String: AnyObject] else { { finish?(error, nil) } return } guard let code = dict["code"] as? Int, code == 200 else { { finish?(error, nil) } return } guard let citys = dict["data"] as? [String]? else { { finish?(error, nil) } return } { finish?(nil, citys) } } }
When there is an error processing and the result is correct, you need to make a callback. The callback may have a bunch of code, which looks like the code will be redundant, and it is easy to forget the callback when handling some errors.
How to write defer
func loadCityList(_ finish: ((Error?, [String]?) -> ())?) { ().async { // Simulate network requests var error: Error? = nil var citys: [String]? = nil defer { { finish?(error, citys) } } let data: AnyObject? // Simulate the data returned by the server guard let dict = data as? [String: AnyObject] else { error = ... return } guard let code = dict["code"] as? Int, code == 200 else { error = ... return } guard let tempCitys = dict["data"] as? [String]? else { error = ... return } citys = tempCitys } }
Using defer not only solves the redundancy of code, but also solves the problem of forgetting callbacks. When we see defer, we know very well that no matter the result of network request, we will callbacks.
Summarize
This article mainly introduces the definition, function and usage of defer
This is the end of this article about the actual application of defer in swift. For more related content of defer in swift, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!