一、在函数体执行结束后按照调用顺序的相反顺序逐个执行。
二、即使函数发生严重错误也会执行。
三、支持匿名函数的调用。
四、通常用于资源清理、文件关闭、解锁以及记录时间等操作。
五、通过与匿名函数配合可在 return 之后修改函数的计算结果。
六、如果函数体内某个变量作为 defer 时匿名函数的参数,则在定义 defer 时即已经获得了拷贝,否则则是引用某个变量的地址。
七、panic 可以在任何地方引发,但 recover 只有在 defer 调用的函数中有效。
八、个人测试,ctrl-C 和 killall 的时候,defer 没有被执行。
例子 1
package main
import "fmt"
func main() {
fmt.Println("f1() = ", f1())
fmt.Println("f2() = ", f2())
//输出
//f1() = 1
//f2() = 0
}
func f1() (result int) {
defer func() {
result++
}()
return 0
}
func f2() (result int) {
return 0
defer func() {
result++
}()
return 0
}
例子 2
package main
import (
"log"
)
func main() {
defer log.Println("11111111")
defer log.Println("22222222")
defer log.Println("33333333")
}
/*
输出:
2018/02/27 09:01:21 33333333
2018/02/27 09:01:21 22222222
2018/02/27 09:01:21 11111111
*/
例子 3
package main
import (
"log"
)
func main() {
defer log.Println("11111111")
defer log.Println("22222222")
defer log.Println("33333333")
panic("I panic in here.")
}
/*
输出:
2018/02/27 09:01:21 33333333
2018/02/27 09:01:21 22222222
2018/02/27 09:01:21 11111111
panic: I panic in here.
goroutine 1 [running]:
main.main()
e:/Go/GOPATH/src/jian/test/test.go:11 +0x172
exit status 2
*/
例子 4
package main
import (
"log"
)
func main() {
defer log.Println("11111111")
f1()
defer log.Println("55555555")
}
func f1() {
defer log.Println("22222222")
defer log.Println("33333333")
panic("I panic in here.")
defer log.Println("44444444") //unreachable code.
}
/*
输出:
$ go run test.go
2018/02/27 09:09:46 33333333
2018/02/27 09:09:46 22222222
2018/02/27 09:09:46 11111111
panic: I panic in here.
goroutine 1 [running]:
main.f1()
e:/Go/GOPATH/src/jian/test/test.go:16 +0x10c
main.main()
e:/Go/GOPATH/src/jian/test/test.go:9 +0x93
exit status 2
*/
例子 5
package main
import (
"fmt"
)
func main() {
for i := 0; i < 3; i++ {
defer func(i int) {
fmt.Println(i)
}(i)
}
}
/*
输出:
2
1
0
*/
例子 6
package main
import (
"fmt"
)
func main() {
for i := 0; i < 3; i++ {
defer func() {
fmt.Println(i)
}()
}
}
/*
输出:
3
3
3
*/
因为 i 没有作为参数传递进去,所以每个闭包函数都是引用同一个 i 变量。
例子 7
package main
func main() {
test()
}
func test() {
println("test start")
defer println("test end")
for i := 0; i < 5; i++ {
println("i =", i)
defer println("defer i =", i)
}
println("end of loop")
}
/*
输出:
test start
i = 0
i = 1
i = 2
i = 3
i = 4
end of loop
defer i = 4
defer i = 3
defer i = 2
defer i = 1
defer i = 0
test end
*/
这个输出说明了在循环体中的 defer 一样要等到函数结束了才会被执行,如果这个 defer 后面是关闭文件的话,那就会有大量的文件描述符被占用,这个时候就不能使用 defer。
测试题
看下是否完全弄懂 defer
package main
import "fmt"
func main() {
var fs = [4]func(){}
for i := 0; i < 4; i++ {
defer fmt.Println("defer i = ", i)
defer func() {
fmt.Println("defer_closure i = ", i)
}()
fs[i] = func() {
fmt.Println("closure i = ", i)
}
}
for _, f := range fs {
f()
}
}
输出:
closure i = 4
closure i = 4
closure i = 4
closure i = 4
defer_closure i = 4
defer i = 3
defer_closure i = 4
defer i = 2
defer_closure i = 4
defer i = 1
defer_closure i = 4
defer i = 0