defer语句会将其对应的函数延迟执行。
defer语句
defer语句用于延迟函数调用,每次会把一个函数压入栈中,函数返回前再把延迟的函数取出并执行。延迟函数可以有参数:
- 延迟函数的参数在defer语句出现时就已确定下来(传值的就是当前值);
- 延迟函数执行按后进先出顺序执行;
- 延迟函数可操作主函数的具名返回值(修改返回值);
释放资源
defer 语句正好是在函数退出时执行的语句,所以使用 defer 能非常方便地处理资源释放、句柄关闭等问题。
func fileSize(filename string) int64 {
f, err := os.Open(filename)
if err != nil {
return 0
}
// 延迟调用Close, 此时Close不会被调用
defer f.Close()
info, err := f.Stat()
if err != nil {
// defer机制触发, 调用Close关闭文件
return 0
}
size := info.Size()
// defer机制触发, 调用Close关闭文件
return size
}
变量捕获
defer中的变量会被提前捕获,后续的修改不会影响到已捕获的值:
func deferTest() {
i := 0
defer fmt.Println("Defer:", i)
i = 10
fmt.Println("Fun:", i)
}
// Fun: 10
// Defer: 0
defer语句中打印的值是修改前的值。
返回值影响
return不是原子操作, 执行过程是: 保存返回值(若有)—>执行defer( 若有) —>执行ret返回调用函数;
有具名返回值时,return可看做是以下语句的结合体:
返回值 = xxx
调用defer函数(依次逆序调用)
空的return
具名返回值
在defer中修改具名返回值时,会影响到函数的实际返回值。
func DeferShow() {
fmt.Println("Out:", deferValueParam())
}
func deferValueParam() (ret int) {
ret = 0
defer func() { // 会直接修改栈中对应的返回值
ret += 10
fmt.Println("Defer Ret:", ret)
}()
ret = 5
fmt.Println("Ret:", ret)
return
}
// Ret: 5
// Defer Ret: 15
// Out: 15
非具名返回值
但是当函数为非具名返回值时,defer将无法影响返回值(因在return时,对应返回值已存入栈中):
func DeferShow() {
fmt.Println("Out2:", deferValueParam())
}
func deferRetValue() int {
ret := 0
defer func() {
ret += 10
fmt.Println("Defer Ret:", ret)
}()
ret = 5
return ret
}
// Defer Ret: 15
// Out2: 5