defer概述:
defer用来声明一个延迟函数,把这个函数放入到一个栈上,当外部的包含方法return之前,返回参数到调用方法之前调用,
也可以说是运行到最外层方法体时调用,我们经常用他来做一些资源的释放,比如关闭io操作。
defer特性:
1.defer定义的延迟函数参数在defer语句定义时就已经确定下来了
2.defer定义顺序与实际执行顺序相反,即先进后出
return特性:
return不是原子操作,执行过程是:保存返回值(若有)-->执行defer(若有)-->执行ret跳转,具体过程如下:
关键字return不是一个原子操作,实际上return只代理汇编指令ret,即将跳转程序执行。return i,实际上分两步进行,
即将i值存入栈中作为返回值,然后执行跳转,而defer的执行时机正是跳转前,所以说defer执行时还是有机会操作返回值的。
defer使用:
在申请资源后调用defer关闭资源
返回前通过defer写日志或者发送通知
关于defer的细节:
defer return 返回值 三者的执行顺序:
1.return 赋值 最先执行,即先将结果写入返回值中;
2.接着defer开始执行一些收尾工作;
3.最后函数携带当前返回值退出(即返回值)。
所以结论是:第一步先return赋值,第二步再执行defer,第三步执行空的return。但是在有名与无名的函数返回值的情况下会有些区别:
如果函数的返回值是无名的(不带命名返回值)如上例中的f2(),则go语言会在执行return指令时,创建一个临时变量保存返回值,最后返回,如下返回 1:
// 这里返回值是无名
func f2() int {
// 第一步:return赋值;创建了一个临时变量保存返回值
n := 1
// 后续defer对n操作,这里的n并不是返回值变量
defer func() {
n++
}()
// 空的return,这一步是将第一步中的临时变量保存的值返回
return
}
有名返回值的函数,由于返回值变量已经提前定义,所以运行过程中并不会再创建临时变量,后续defer操作的变量都是返回值变量,如下返回 2:
// 定义了返回值变量
func f1() (n int) {
// 直接操作返回值
n = 1
defer func() {
// 这里操作的也是返回值
n++
}()
return n
}
参考链接: http://liuqh.icu/2023/05/18/go/bottom/6-defer/
