golang defer 学习笔记

Posted by Jason on Saturday, March 16, 2019

TOC

defer使用中注意事项:

defer 函数执行顺序

  • defer执行函数是按后进先出(栈)顺序执行。defer中遇到函数为 nil 指针时,会抛出 panic ,但是不影响后面函数的执行。
func main() {
    fmt.Println("vim-go")
    var fun func()

    defer fmt.Println("befor defer1")
    defer fmt.Println("befor defer2")
    defer fun()
    fmt.Println("normal ")
    defer fmt.Println("after defer")
}

输出结果

vim-go
normal
after defer
befor defer2
befor defer1
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x1050358]

而如果,defer 的是一个指针对象A的函数,切A为空指针则会立即panic:

func (t test) Print(s string) {
    fmt.Println(s)
}

func main() {
    fmt.Println("vim-go")
    var t *test

    defer fmt.Println("befor defer1")
    defer fmt.Println("befor defer2")
    defer t.Print("1111")
    fmt.Println("normal ")
    defer fmt.Println("after defer")
}

输出

vim-go
befor defer2
befor defer1
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x1091635]

这说明,针对对象函数,在defer声明时进行解函数操作。而在函数退出时,进行函数调用。

官方解释如下:

If a deferred function value evaluates to nil, execution panics when the function is invoked, not when the “defer” statement is executed.

意思是:如果 defer 的函数是 nil ,在函数调用的时候抛出panic,而不是 defer 声明的时候。

defer 延迟执行

  • defer 属于延迟函数,会在函数退出时才执行。所以对于某些占用资源的操作,需要考虑用闭包来使用。

例如:

for _, fileName := range fileList {
    f := os.Open(fileName)
    defer f.Close //文件资源直到函数退出才释放
}

可以修改为:

for _, fileName := range fileList {
    func() {
        f := os.Open(fileName)
        defer f.Close //文件资源直到函数退出才释放
    } ()
}

defer 函数变量

  • defer 函数为闭包,且引用了外部变量的情况,则会在defer函数真正调用时根据整个上下文确定当前的值。如果是传参函数,则在传参时已经确定了变量的值。

例子:

for i := range [3]test{} {
    defer func() {
        fmt.Print(i)
    }()
}

输出:

2
2
2

结论:针对闭包引用变量,是在 defer 执行函数时才确定变量值。

defer 和 return 执行顺序

  • defer 执行顺序。go 中 return 指令,可以拆解为:
    • 返回值 = xxx
    • defer函数调用
    • return 空

例子:

fun f() {
    t := 1
    defer func() {
        t = t + 1
        fmt.Println("defer func, t is ", t)
    }()
    t += 2
    return t
}

func main() {
    fmt.Println("main f return: ", f())
}

返回结果:

defer func, t is  4
main f return:  3

上面 f 函数可以拆解为:

fun f() (r int){
    t := 1
    defer func() {
        t = t + 1
        fmt.Println("defer func, t is ", t)
    }()
    t += 2
    r = t
    return
}

即函数返回值赋值,在 defer 函数执行前完成。

  • 对于有返回值的函数,不能通过defer来执行,特殊情况

「真诚赞赏,手留余香」

Jason Blog

真诚赞赏,手留余香

使用微信扫描二维码完成支付


comments powered by Disqus