golang中defer的些许总结
引言
最近自己在学习go项目的时候,查看别人的源码经常发现defer
的使用,加上自己之前对这个关键字没怎么在意,只知道差不多是用来释放内部变量的。
查看资料可以发现,defer
会在当前函数或者方法返回之前执行传入的函数,经常用于关闭文件描述符、关闭数据库连接以及解锁资源。
为了能够更形象地理解defer
的行为,我就在这贴上一段关闭文件描述符的示例代码:
func CopyFile(source, dest string) bool {
if source == "" || dest == "" {
log.Println("source or dest is null")
return false
}
source_open, err := os.Open(source)
if err != nil {
log.Println(err.Error())
return false
}
defer source_open.Close()
dest_open, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY, 644)
if err != nil {
log.Println(err.Error())
return false
}
defer dest_open.Close()
_, copy_err := io.Copy(dest_open, source_open)
if copy_err != nil {
log.Println(copy_err.Error())
return false
} else {
return true
}
}
在示例代码中,使用了defer
来关闭文件流,用来避免忘记文件释放的问题。
defer的调用时机以及多次调用defer的执行顺序
defer
传入的函数会在当前调用方法返回之前得到运行。
为了更好地解释上面的原理,通过一个简单例子来观察就很明显了:
func main() {
for i := 0; i < 4; i++ {
defer fmt.Println(i)
}
}
示例程序中是用来循环输出数字的,实际上的运行结果如下,可以观察得出defer
的效果类似于栈的先进后出
:
3
2
1
0
defer的预计算参数
defer继承了Go中函数调用的特性,就是函数调用都是传值的。所以,使用defer关键字之后,会立刻对函数中引用的外部参数进行拷贝
示例程序如下,最后包含了输出结果:
func PrintInfo() {
val := 0
fmt.Println("use defer to print val-->")
// in here, will output 0
defer fmt.Println(val)
val++
// in here, will output 1
defer fmt.Println(val)
return
}
use defer to print val-->
1
0
至于程序为什么会输出1 0 而不是 0 1 ,答案已经在上面有了解释;