defer是Go语言提供的关键字用来调度一个函数(被延期的函数),使其在执行defer的函数即将返回之前才被运行被延期执行的函数,它的参数(包括接受者)在defer执行的时候被求值的,而不是在调用执行的时候。也就是说被延期执行的函数的参数是按正常顺序被求值的。
defer常用来释放资源,如果有多个defer表达式,调用顺序类似于栈,越后面的defer表达式越先被调用。defer 函数调用的执行时机是外层函数设置返回值之后, 并且在即将返回之前。
defer应用
1、当defer被声明时,参数被实时解析
package main import "fmt" func oldboy() (str string) { str = "oldboy" defer fmt.Printf("defer : %sn", str) str = "老男孩IT教育,只培养技术精英。" return } func main() { oldboy() }
运行结果:
defer : oldboy
通过运行结果,可以看到 defer 输出的值,就是定义时的值。
2、defer执行顺序为先进后出。
package main import "fmt" func test() { for i := 1; i < 4; i++ { defer fmt.Printf("defer%dn", i) } } func main() { test() }
3、defer 可以读取有名返回值
package main import ( "fmt" ) func test() (i int) { defer func() { i += 100 }() return 1 } func main() { ret := test() fmt.Println(ret) }
运行结果:
101
需要明确的是 defer 代码块的作用域仍然在函数之内,结合上面的函数也就是说,defer 的作用域仍然在 test() 函数之内。因此defer仍然可以读取 test() 函数内的变量。
当执行return 1 之后,i 的值是 1 。此时defer代码块开始执行 i += 100操作。 因此最后输出 101。
defer陷阱
1、defer 与 closure
package main import ( "errors" "fmt" ) func test(x, y int) (z int, err error) { defer fmt.Printf("first defer err %vn", err) defer func(err error) { fmt.Printf("second defer err %vn", err) }(err) defer func() { fmt.Printf("third defer err %vn", err) }() if y == 0 { err = errors.New("divided by zero!") return } z = x / y return } func main() { test(2, 0) }
运行结果:
third defer err divided by zero! second defer err <nil> first defer err <nil>
解释:如果 defer 后面跟的不是一个 closure 最后执行的时候我们得到的并不是最新的值。
2、defer 与 return
package main import "fmt" func test() (i int) { i = 0 defer func() { fmt.Println(i) }() return 2 } func main() { test() }
输出结果:
2
解释:在有具名返回值的函数中(这里具名返回值为 i),执行 return 2 的时候实际上已经将 i 的值重新赋值为 2。所以defer closure 输出结果为 2 而不是 1。
3、defer nil 函数
package main import ( "fmt" ) func test() { var run func() = nil defer run() fmt.Println("runs") } func main() { defer func() { if err := recover(); err != nil { fmt.Println(err) } }() test() }
输出结果:
runs runtime error: invalid memory address or nil pointer dereference
解释:名为 test 的函数一直运行至结束,然后 defer 函数会被执行且会因为值为 nil 而产生 panic 异常。然而值得注意的是,run() 的声明是没有问题,因为在test函数运行完成后它才会被调用。
特别声明:以上文章内容仅代表作者本人观点,不代表变化吧观点或立场。如有关于作品内容、版权或其它问题请于作品发表后的30日内与变化吧联系。
- 赞助本站
- 微信扫一扫
-
- 加入Q群
- QQ扫一扫
-
评论