Check out apple’s official presentation

Use the defer statement to execute a series of statements as you leave the current code block. This statement allows you to perform some of the cleanup necessary to leave the current code block in any way — whether by throwing an error or by a statement such as return or break. For example, you can use the defer statement to ensure that file descriptors are closed and that 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 deferred. Statements that are delayed cannot contain any control transition statements, such as break, return, or throw an error. Deferred actions are executed backwards 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 last, and so on. The last statement is executed first.

To summarize apple’s official presentation

  • The defer statement executes before the code block (methods, closures, and so on, wrapped in braces) exits the scope; color{red}{before the scope exits the scope; that is, the code in defer is executed after everything else in the code block has been executed
  • A code block allows multiple defers to be executed in the order from back to front \color{red}{from back to front} from back to front

Some testing and error correction

Test Case 1
Func testDefer() {defer {print(" defer in method ")} if true {defer {print(" defer in method ")} print(" defer in method ")} Print (" code in method ") if true {return} print(" last line before method ends ")} testDefer()Copy the code

The above code prints the result:

The last code in if defer content in the code method in the if content methodCopy the code

In the printed result, the code in the first if and its defer are executed first, and the method’s defer is executed last. As you can see, the rest of the code in the block is executed first, and the content of defer is executed last. The scope of defer is not simply a method, but a code block (some students might make this mistake).

Test Case 2
Func testDefer() {print(" start ") defer {print("defer 1 ")} defer {print("defer 2 ")} if true {return} defer {print("defer 3 ")} print(" last line before method end ")} testDefer()Copy the code

Print the result

Start to defer what's in 2 and what's in 1Copy the code

We can see that the last defer didn’t execute, so the location defined by defer is important. If the code defined by defer isn’t executed, the rest of the defer will not be executed until the block ends

Some practical application scenarios

Scenario 1: Some resources need to be released when 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 File.readline () {// Process files. } // Close (file) is called here, at the end of the scope. }}Copy the code

Use defer to release the resource as soon as you start using it so you don’t forget to release it

Scenario 2: Lock to unlock, borrowed from Kingfisher
let lock = NSLock()
func testDefer() {
    lock.lock()
    defer {
        lock.unlock()
    }
    
    doSomething()
}
testDefer()
Copy the code

Use defer to unlock immediately after locking to avoid forgetting to unlock

Scenario 3: Handle repeated operations before the end of the code block scope, such as when requesting network data

The usual way of writing it

func loadCityList(_ finish: ((Error? , [String]?) - > ())? {dispatchqueue.global ().async {// Let data: AnyObject? // Guard let dict = data as? [String: AnyObject] else { DispatchQueue.main.async { finish? (error, nil) } return } guard let code = dict["code"] as? Int, code == 200 else { DispatchQueue.main.async { finish? (error, nil) } return } guard let citys = dict["data"] as? [String]? else { DispatchQueue.main.async { finish? (error, nil) } return } DispatchQueue.main.async { finish? (nil, citys) } } }Copy the code

Callbacks need to be done every time there is an error handling and the result is correct, and callbacks can have a bunch of code that looks redundant, and it’s easy to forget callbacks in some error handling situations

How to write defer

func loadCityList(_ finish: ((Error? , [String]?) - > ())? {dispatchqueue.global ().async {// Simulate network request var error: error? = nil var citys: [String]? = nil defer { DispatchQueue.main.async { finish? (error, citys) } } let data: AnyObject? // 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 } }Copy the code

Using defer solved both code redundancy and the problem of forgetting to call back, and when we saw defer, it was clear that the callback would happen regardless of the result of the network request

conclusion

  • This article focuses on the definition of defer, its functions, and some of its uses