day1 defer 延迟注销 | 青训营

67 阅读1分钟

defer 的作用

在golang中提供了defer关键字,在一个函数中可以去注册多个延迟调用,这些调用以先进后出(FILO)的顺序在函数返回前被执行,可以保证一些资源可以被回收或释放。但是如果错误的使用defer去处理返回值,可能会有一些意想不到的后果。

例子

// 带命名返回值的函数
func f1() (r int) {
    defer func() {
        r++
    }()
    return 1
}


// 不带命名返回值的函数
func f2() int {
    t := 1
    defer func() {
        t ++
    }()
    return t
}


func TestF1(t *testing.T) {
    fmt.Println(f1()) // r = 2
}


func TestF2(t *testing.T) {
    fmt.Println(f2()) // r = 1
}

首先我们需要明确两点

  1. 函数调用方负责开辟栈空间,包括形参和返回值的空间。
  2. 有名的函数返回值相当于函数的局部变量,被初始化为类型的零值。

在这里解释一下 f1

  1. r 是函数的有名返回值,分配在栈上,初始化为 0。
  2. return 1 会将 1 复制到返回值栈区,r被赋值为 1。
  3. 执行defer语句,由于匿名函数对返回值r是闭包引用,所以r++执行后,函数返回值被修改为 2。
  4. defer 执行完后 RET 返回,此时函数的返回值为 2。

在看一下 f2, 他与 f1 有什么不同?

  1. 引入局部变量 t = 1
  2. 复制 t 的值到返回值(r)所在的栈区
  3. defer 语句的匿名函数对局部变量 t 做修改, t = 2
  4. 函数返回, 返回值(r)仍然为 1

image.png