在释放局部资源时, 可以用defer管理

Go语言版本基于deferMutex用法

func safeRead(Mutex *mu) []byte {
mu.Lock()
defer mu.Unlock()
return read();
}

  

对于可能申请失败的资源也很好处理:

func loadFile(name string) ([]byte, error) {
f, err := os.Open(name)
if err != nil {
return nil, err
}
defer f.Close()
return load(f)
}

使用defer语句, 可以方便地组合函数/闭包和资源对象. 即使panic时, defer也能保证资源的正确释放.

defer

官方给出的文档上介绍defer的执行有三条基本规则:

1. defer函数是在外部函数return后,按照后申明先执行(栈)的顺序执行的;
package main

import "fmt"

func main() {
defer fmt.Println("1")
defer fmt.Println("2")
defer fmt.Println("3")
}

  

输出是

3
2
1
2. defer函数的参数的值,是在申明defer时确定下来的;

看几个例子吧,注意第一条规则。
这是一个普通类型的例子:

package main

import "fmt"

func main() {
i := 0 defer fmt.Println(i) // 这也算是作为defer函数的参数
defer func(j int) { fmt.Println(j) }(i) // 作为参数
defer func() { fmt.Println(i) }() // 作为闭包(closure)进行引用 i++
}

  

输出是:

1
0
0

如果是引用类型也是一样的道理:

  • 当修改引用对象的属性时:
  • package main
    
    import "fmt"
    
    type Person struct {
    name string
    } func main() {
    person := &Person{"Lilei"} defer fmt.Println(person.name) // person.name作为普通类型当做defer函数的参数
    defer fmt.Printf("%v\n", person) // 引用类型作为参数
    defer func(p *Person) { fmt.Println(p.name) }(person) // 同上
    defer func() { fmt.Println(person.name) }() // 闭包引用,对引用对象属性的修改不影响引用 person.name = "HanMeimei"
    }

      

    输出是:

    HanMeimei
    HanMeimei
    &{HanMeimei}
    Lilei
  • 当修改引用本身时:
package main

import "fmt"

type Person struct {
name string
} func main() {
person := &Person{"Lilei"} defer fmt.Println(person.name) // 同上,person.name作为普通类型当做defer函数的参数
defer fmt.Printf("%v\n", person) // 作为defer函数的参数,申明时指向“Lilei”
defer func(p *Person) { fmt.Println(p.name) }(person) // 同上
defer func() { fmt.Println(person.name) }() // 作为闭包引用,随着person的改变而指向“HanMeimei” person = &Person{"HanMeimei"}
}

  

输出是:

HanMeimei
Lilei
&{Lilei}
Lilei 在defer函数申明时,对外部变量的引用是有两种方式的,分别是作为函数参数和作为闭包引用。作为函数参数,则在defer申明时就把值传递给defer,并被cache起来。作为闭包引用的话,则会在defer函数执行时根据整个上下文确定当前的值。
3. defer函数可以读取和修改外部函数申明的返回值。

同样给一个例子

package main

import "fmt"

func main() {
fmt.Printf("output: %d\n", f())
} func f() (i int) {
defer fmt.Println(i) // 参数引用
defer func(j int) { fmt.Println(j) }(i) // 同上
defer func() { fmt.Println(i) }() // 闭包引用
defer func() { i++ }() // 执行前,i=2
defer func() { i++ }() // 执行前,i=1 i++ return
}

  

有了之前的基础,这个输出可以轻松推测出来:

3
0
0
output: 3

defer的基本规则就这三条了。了解之后,瞬间明朗了。

最开始的答案

通过defer的三条规则(其实只要前两条)就能知道输出了:

3
3
3
0
 
 

最新文章

  1. POJ1002
  2. iOS学习之iOS沙盒(sandbox)机制和文件操作(二)
  3. php SimpleXML 例子
  4. [MFC] VS2013版本MFC工程移植到VC6.0上
  5. Oracle IF & CASE语句
  6. voxel 与 pixel
  7. Swift的文档注释
  8. 在Egret实现二维码长按识别
  9. windows下phpstorm的快捷键
  10. HDU 5086
  11. Html.DropDownListFor
  12. Linux 根文件系统的制作
  13. 织梦dedecms源码安装方法
  14. CSS 控制应为Html页面高度导致抖动的问题
  15. unity3D HTC VIVE开发-物体高亮功能实现
  16. sql 查询某个条件多条数据中最新的一条数据或最老的一条数据
  17. mysql获取表中数据行数
  18. redis中key过期事件
  19. Romantic---hdu2669(扩展欧几里德模板)
  20. C#回调实现的一般过程

热门文章

  1. [Web] 深入理解现代浏览器
  2. python gis库
  3. JPA的查询方法总结
  4. css3中的盒子模型
  5. SQLite R*Tree 模块测试
  6. Python3基础 内置函数 eval str转为list tuple dict
  7. 【PHP】使用phpoffice/phpexcel导入导出数据
  8. SVM – 线性分类器
  9. phpspreadsheet 中文文档(二) 结构+自动筛选
  10. 【MongoDB学习之六】MongoDB集群