网上内容摘录学习
defer基础使用
此段落参考 https://my.oschina.net/chai2010/blog/140065
- 资源回收,比如file,mutex
f, err := os.Open("file")
if err != nil {
panic(err)
}
defer f.Close()
mu.Lock()
defer mu.Unlock()
- panic的捕获
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in f", r)
}
}()
- 返回值的修改
func F() (i int) {
defer func() {
i++
}()
return 1
} // 返回值为2
defer的实际调用时机
此段落参考 https://tiancaiamao.gitbooks.io/go-internals/content/zh/03.4.html
defer是在return之前执行的。
这是官方原话说的,实际上,
这个return应该分三个步骤:
返回值 = xxx
调用defer函数
空的return
举个例子:
func f() (i int) {
j := 1
defer func() {
j = j + 1
}()
return j
}
- i = j
- j = j + 1
- return
所以,函数的返回值实际是1
defer三大特性
此段落参考 https://my.oschina.net/chai2010/blog/119216
- 当defer调用函数的时候, 函数用到的每个参数和变量的值也会被计算
在这个例子中, 表达式"i"的值将在defer fmt.Println(i)
时被计算. Defer将会在 当前函数返回的时候打印"0"
func a() {
i := 0
defer fmt.Println(i)
i++
return
}
- Defer调用的函数将在当前函数返回的时候, 以后进先出的顺序执行.
下面的函数将输出"3210":
func b() {
for i := 0; i < 4; i++ {
defer fmt.Print(i)
}
}
- Defer调用的函数可以在返回语句执行后读取或修改命名的返回值.
在这个例子中, defer语句将会在当前函数返回后将i增加1. 实际上, 函数会返回2:
func c() (i int) {
defer func() {
i++
}()
return 1
}
学习后的思考
主要是对特性1的思考
当defer调用函数的时候, 函数用到的每个参数和变量的值也会被计算
主要是考虑,指针传入的情况
直接上代码:
func F1() {
var a *int
var c int = 6
a = &c
defer func(a *int) {
fmt.Println(*a)
}(a)
var b int = 5
a = &b
} // 此时打印的是:6
- 此段代码中,defer调用时,a指针指向的是c的地址,
后面再改变a的地址,defer中已经存起来的a的地址都不会改变了。
相当于a把地址拷贝给了defer中的备份a。
func F2() {
var a *int
var c int = 6
a = &c
defer func() {
fmt.Println(*a)
}()
var b int = 5
a = &b
} // 此时打印的是:5
- 此段代码中,defer直接使用了a的地址,是a在return时候,所指向的实际地址。
func F3() {
var a int = 0
defer func(a *int) {
fmt.Println(*a)
}(&a)
a = 5
} // 此时打印的是:5
- 此段代码中,传入了a的地址,后面修改该地址上的值,是会起作用的。
另外两个小点:
- defer是会稍微消耗性能,大概100~200ns
- defer在panic的情况下,也会调用的
补充:
在学习effect go时,学习到:
defer中参数是函数时,该函数会先执行
func main() {
T11()
}
func T11() {
s := "a"
defer T12(T13(s))
fmt.Println("T11", s)
}
func T12(s string) string {
fmt.Println("T12", s)
return s
}
func T13(s string) string {
fmt.Println("T13", s)
return s
}
// 结果
T13 a
T11 a
T12 a