defer 也要小心使用,带来的bug
defer 很简单,但也不免大意忘记写出BUG,下面这4种情况需要注意。
1、defer 参数是"立即求值"
go
func main() {
i := 1
defer fmt.Println(i) // 函数运行最后输出i的值
i = 2
}
输出1, 原因是defer方法的参数是实时计算
2、方法接收者拷贝
go
type Car struct {
name string
}
func (c Car) Print() {
fmt.Println(c.name)
}
func main() {
c := Car{name: "Mary"}
defer c.Print() // 最后再调用Print输出
c.name = "Tom"
}
输出的并不是Tom,因为即使调用方法是接收器,但它前面的值也会被立即计算,所有defer处就拷贝了c,与第1点原理一样。
3、误认为是作用域名结束执行
go
for i := 0; i < 5000; i++ {
f, _ := os.Open(fmt.Sprintf("file_%d.csv", i))
defer f.Close()
}
所有 Close() 都在函数结束才执行,可能造成资源浪费或文件句柄可能被耗尽。
4、未注意defer执行顺序
go
defer fmt.Println("A")
defer fmt.Println("B")
defer fmt.Println("C")
输出的是C、B、A,defer是一个栈,先入后出。