1. 基础特性

1.1 先进后出

对于多个defer语句,类似栈,按先进后出的顺序执行。

1func main() {
2    var whatever [5]struct{}
3    for i := range whatever {
4        defer fmt.Print(i)
5    }
6}
7
8// 43210

1.2 实时解析函数参数

 1package main
 2
 3import "fmt"
 4
 5func test(a int) {//无返回值函数
 6	defer fmt.Println("1、a =", a) //方法
 7	defer func(v int) { fmt.Println("2、a =", v)} (a) //有参函数
 8	defer func() { fmt.Println("3、a =", a)} () //无参函数
 9	a++
10}
11func main() {
12	test(1)
13}
14
15// 3、a = 2
16// 2、a = 1
17// 1、a = 1

1.3 return 返回机制

defer、return、返回值三者的执行逻辑应该是:

  • 首先,return 最先执行,return将结果写入返回值中;
  • 接着,defer 执行收尾工作;
  • 最后,函数携带当前返回值(可能和最初的返回值不相同)退出。

defer 在 return 后

defer放在return后面时,不会被执行。

1func f(i int) int{
2	return i
3	defer fmt.Print("i =", i)
4	return i+1
5}
6
7func main() {
8	f(1)
9}

没有输出,因为return i之后函数就已经结束了,不会执行defer。

无名返回值 🎓

 1func a() int {
 2	var i int
 3	defer func() {
 4		i++
 5		fmt.Println("defer2:", i) 
 6	}()
 7	defer func() {
 8		i++
 9		fmt.Println("defer1:", i) 
10	}()
11	return i
12}
13
14func main() {
15	fmt.Println("return:", a()) 
16}
1defer1: 1
2defer2: 2
3return: 0

返回值由变量 i 赋值,相当于返回值=i=0。

第二个defer中i++ = 1, 第一个defer中i++ = 2,所以最终i的值是2。

但返回值已经被赋值了,即使后续修改i也不会影响返回值。最终返回值返回,所以main中打印0。

有名返回值 🎓

 1func b() (i int) {
 2	defer func() {
 3		i++
 4		fmt.Println("defer2:", i)
 5	}()
 6	defer func() {
 7		i++
 8		fmt.Println("defer1:", i)
 9	}()
10	return i //或者直接写成return
11}
12
13func main() {
14	fmt.Println("return:", b())
15}
1defer1: 1
2defer2: 2
3return: 2

此处指明了返回值就是i,所以后续对 i 进行修改都相当于在修改返回值,所以最终函数的返回值是2。

特殊情况

注意下列代码,返回值 r 作为参数传入 defer,虽然有 r = r + 5 ,但这是局部变量,与 f() 中的返回值 r 无关,因此该函数仍然返回 1

1func f() (r int) {
2    defer func(r int) {
3          r = r + 5
4    }(r)
5    return 1
6}

函数返回值为地址

本质上和有名返回值一样。

 1func c() *int {
 2	var i int
 3	defer func() {
 4		i++
 5		fmt.Println("defer2:", i)
 6	}()
 7	defer func() {
 8		i++
 9		fmt.Println("defer1:", i)
10	}()
11	return &i
12}
13
14func main() {
15	fmt.Println("return:", *(c()))
16}
1defer1: 1
2defer2: 2
3return: 2

2. Defer 与闭包

for 结束时 t.name="c",接下来执行的那些defer语句中用到的t.name的值均为"c"

 1type Test struct {
 2	name string
 3}
 4func (t *Test) pp() {
 5	fmt.Println(t.name)
 6}
 7func main() {
 8	ts := []Test{{"a"}, {"b"}, {"c"}}
 9	for _, t := range ts {
10		defer t.pp()
11	}
12}
13// c
14// c
15// c

修改代码,使用有参参数

defer语句中的参数会实时解析,所以在碰到defer语句的时候就把该时的t代入了。

 1type Test struct {
 2	name string
 3}
 4func pp(t Test) {
 5	fmt.Println(t.name)
 6}
 7func main() {
 8	ts := []Test{{"a"}, {"b"}, {"c"}}
 9	for _, t := range ts {
10		defer pp(t)
11	}
12}
13// c
14// b
15// a

修改代码,在循环中声明新变量

每次都有一个新的变量tt:=t,所以每次在执行defer语句时,对应的 tt 不是同一个,所以输出的结果也不相同。

 1type Test struct {
 2	name string
 3}
 4func (t *Test) pp() {
 5	fmt.Println(t.name)
 6}
 7
 8func main() {
 9	ts := []Test{{"a"}, {"b"}, {"c"}}
10	for _, t := range ts {
11		tt := t
12		println(&tt)
13		defer tt.pp()
14	}
15}
10xc000010200
20xc000010210
30xc000010220
4c
5b
6a

3. Defer 常见使用场景

3.1 关闭文件

开启文件后,使用 defer 关闭文件。

1func ReadFile(filename string) ([]byte, error) {
2    f, err := os.Open(filename)
3    if err != nil {
4        return nil, err
5    }
6    defer f.close()
7    return ReadAll()
8}

3.2 互斥锁

加锁后,使用 defer 解锁。

1var mu sync.Mutex
2var m = make(map[string]int)
3 
4func lookup(key string) int {
5    mu.Lock()
6    defer mu.Unlock()
7    return m[key]
8}

调用os.Exit时defer不会被执行

1func deferExit() {
2    defer func() {
3        fmt.Println("defer")
4    }()
5    os.Exit(0)
6}

参考资料

  1. https://blog.csdn.net/Cassie_zkq/article/details/108567205