goland 详解 defer
defer
用来声明一个延迟函数,把这个函数放入到一个栈上, 当外部的包含方法return之前,返回参数到调用方法之前调用,也可以说是运行到最外层方法体的”}”时调用。我们经常用他来做一些资源的释放,比如关闭io操作
func doSomething(fileName string) { file,err := os.Open(fileName) if err != nil { panic(err) } defer file.Close() } 复制代码
defer
可以保证方法可以在外围函数返回之前调用。有点像其他言的 try finally
try{ }finally{ } 复制代码
defer 读写外部变量
defer
声明的函数读写外部变量,和闭包差不多。比如下面的代码
func doSomething() { v := 10 defer func() { fmt.Println(v) v++ fmt.Println(v) }() v += 5 } 复制代码
输出为
15 16 复制代码
就像 闭包
一样,如果不是 defer
函数方法内的变量会向上一层函数访问变量,重新做计算。
defer 读写命名的返回值
这个例子中, defer
声明的方法,给命名的返回值自增1
1 func doSomething() (rev int) { 2 defer func() { 3 rev++ 4 }() 5 6 return 5 7 } 复制代码
第6行
的 return
相当于
return rev = 5 复制代码
defer
声明的匿名函数会在 return
之前执行,相当于
rev = 5 // 执行defer方法 rev++ //然后return return 复制代码
所以结果是 6
我把代码做一点点修改
1 func doSomething() (rev int) { 2 v := 10 3 defer func() { 4 v++ 5 }() 6 7 return v 8 } 复制代码
第7行返回的是局部变量v.
return v 相当于 return rev = v 复制代码
defer
函数里是对局部变量 v
的操作,所以与返回的 rev
没有关系。
所有执行的结果是: 10
defer 执行顺序
当有多个defer时执行顺序逆向的,后进先出:
func doSomething() { defer fmt.Println(1) defer fmt.Println(2) } 复制代码
会先输出2,再输出1
defer 处理异常
panic抛出异常后,如果不处理应用程序会崩溃。为了防止程序崩溃,我们可以在 defer
的函数里使用 recover
来捕获中异常:
func doSomething() { defer func() { if err := recover(); err != nil { fmt.Print(err) } }() fmt.Println("Running...") panic("run error") } 复制代码
输出:
Running... run error 复制代码
recover
会捕获 panic
的异常。我再把代码做一点点修改:
func doSomething() { defer func() { if err := recover(); err != nil { fmt.Print(err) } }() defer func() { panic("defer error") }() fmt.Println("Running...") panic("run error") } 复制代码
输出结果
Running... defer error 复制代码
因为 recover()
只捕获最后一次 panic