本篇主要记录defer的三个关键点。
我们知道defer调用会在函数结束时被执行,但除此之外,我们还应该弄清楚几个问题:
函数结束前遇到panic,defer调用还会执行吗?
多个defer的调用顺序是怎么样的?
defer的参数是调用时确定的还是执行时?
下面结合例子,逐个说明一下。
遇到panic时,panic之前的defer会执行
其实这点不算是defer的知识点,算是panic的流程。程序发生panic时,会一层一层的返回,查找有没有recover,返回的过程中遇到的所有defer都会执行,因为recover一定是在defer调用的。可以写段简单的程序试验下。
package main
import "fmt"
func print(a int) {
fmt.Println(a)
}
func main() {
a, b := 2, 0
defer print(a)
fmt.Println(a / b)
defer print(b)
}
运行结果如下:
a/b时,由于b是0,所以出现了panic,但是panic上面defer调用的print(a)还是执行了,而panic后面的print(b)则没有执行。
多个defer,按照后进先出的顺序执行
defer在调用时,会将执行的内容先存入栈中,到函数执行结束,再将栈中的调用依次取出执行。
我们试验一下,先defer print(a),再defer print(b)。
package main
import "fmt"
func print(a int) {
fmt.Println(a)
}
func main() {
a, b := 2, 0
defer print(a)
defer print(b)
fmt.Println("main is running")
}
运行结果如下:
可以看到,print(b)在print(a)之前被执行了。
defer调用的参数,是调用时确定的
defer调用如果传入了一个变量,而这个变量在函数运行过程中被改变了,等到函数结束执行defer调用的时候,defer调用的参数,还是调用的时候那个值,这是因为变量的值和defer调用一起,在调用的时候,已经存入了栈中。
还是简单验证一下。
package main
import "fmt"
func print(a int) {
fmt.Println(a)
}
func main() {
a := 2
defer print(a)
a = 3
fmt.Println("main is running, a=", a)
}
运行结果如下:
可以看到a虽然被改变了,但是最后执行的print(a),打印的还是defer调用时候的值。