defer是go中的一个关键字, 我们可以用该关键字实现函数退出时的资源关闭、回收等操作。 这里主要是学习下defer的执行顺序。
defer的语句按照添加的顺序逆序执行,即先进后出。 然后最后是函数返回。
但是还要注意一点的是defer的执行时机是:
1、外层函数设置返回值之后,并且在即将返回之前。
2、return xxx 操作并不是原子的。
这里三个代码实例和结果看下defer的执行情况
package main
import ("fmt")
func num1() int {
var i int
defer func() {
i++
fmt.Println("defer 2 : ", i)
}()
defer func() {
i++
fmt.Println("defer 1 : ", i)
}()
return i
}
func num2() (i int) {
defer func() {
i++
fmt.Println("defer 2 : ", i)
}()
defer func() {
i++
fmt.Println("defer 1 : ", i)
}()
return i
}
func num3() *int {
var i int
defer func() {
i++
fmt.Println("defer 2 : ", i)
}()
defer func() {
i++
fmt.Println("defer 1 : ", i)
}()
return &i
}
func main() {
fmt.Printf("for num 1 return is \n")
fmt.Printf("return is %d \n", num1())
fmt.Printf("for num 2 return is \n")
fmt.Printf("return is %d \n", num2())
fmt.Printf("for num 3 return is \n")
fmt.Printf("return is %d \n", *num3())
}
上面代码的执行结果是:
for num 1 return is
defer 1 : 1
defer 2 : 2
return is 0
for num 2 return is
defer 1 : 1
defer 2 : 2
return is 2
for num 3 return is
defer 1 : 1
defer 2 : 2
return is 2
分析一下原因:
函数num1: 因为i变量是函数内不定义的。 return的时候已经把i的值返回了。 后面defer函数操作的是i已经跟返回的无关了。 所以return的是0 defer分别打印的是再增加之后的1和2.
函数num2: 因为i变量是函数调用时传入的。 return 返回的i和后面defer继续操作的仍然是同一个i, 所以defer 对i的改变仍然作用到了返回的那个i上面。
函数num3: 因为返回的是指针, 虽然var是在内部定义的,但是在返回的时候直接把这个返回出去了,所以defer后续的改变仍然也做用到了返回的那个i上面。
这点在C C++语言中是不可以的,因为其局部变量默认是栈空间的,而栈空间资源会再函数结束之后被回收。而在go中,个人理解是其自动管理的内存分配,其会判断变量恩声明周期而处理,所以返回指针也是安全的。