结论

defer 是在函数代码体执行完之后,函数返回之前执行的,多个 defer 之间按照后进先出(LIFO)的顺序执行。
这里还有个问题需要说明,return 并非原子操作,所以在有 defer 的情况下,函数的执行分为4个阶段,分别如下:

  1. 真正的函数体执行业务逻辑
  2. 函数返回值赋值,就是将真正的函数体执行结果赋值给返回值
  3. defer 函数要执行的业务逻辑,注意这里 defer 中函数处理中有可能会修改返回值
  4. 执行 return 整个函数调用栈结束

示例

  • 示例1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import "fmt"

func f1() int {
x := 5
fmt.Println("set value")
defer func() {
x++
fmt.Println("defer")
}()
return x // set value -> defer -> return
}

func main() {
fmt.Println(f1()) // 这里因为 x 变量 在 `defer` 函数内部属于 局部变量,所以返回值 x 没有变化,还是函数体内初始化的值
}
  • 示例2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import "fmt"

func f2() (x int) {
fmt.Println("set value")
defer func() {
x++
fmt.Println("defer")
}()
return 5 // set value -> defer -> return
}

func main() {
fmt.Println(f1()) //输出6,从这里我们可以看出,执行顺序是在 `return` 之前,因为 `defer` 修改了返回的值,而且 `defer` 中执行 `x++` 的结果是5,也就是说,在执行 `x++` 之前,`x` 的值就是 `5`了,这也证明了在 `return` 之前执行了 `x = 5` 的赋值操,所以 `defer` 是在给返回之前进行的赋值操作,而且是分两步进行的,非原子操作
}
  • 示例3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import "fmt"

func f3() (x int) {
fmt.Println("set value")
defer func(x int) {
x++
fmt.Printf("defer")
}(x)
fmt.Printf("func")
return 5 // set value -> func -> defer -> return
}

func main() {
fmt.Println(f1()) // 输出 5,因为在 `defer` 后的匿名函数传递了 `x` 变量是命名返回值的副本,本质上 `defer` 中的参数 `x` 和命名返回值的 `x` 不是一个变量,只是恰巧同名而已,所以在 `defer` 后的匿名函数修改的是副本的值,不影响命名函数的返回值
}
  • 示例4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import "fmt"

func f4() (x int) {
defer func(x *int) {
*x++ // 由于引用传值,所以*x++
fmt.Printf("defer:%v\n", &x)
}(&x)
fmt.Printf("func:%v\n", &x)
return 5 // func:xxx -> defer:xxx -> return
}

func main() {
fmt.Println(f4()) // 6 传递命名返回值的引用,所以 `x` 会加1
}