defer的执行顺序与时机

--write by zhuwx 2019-10-21 10:34:55 +0800 CST

我们知道defer会在函数结束时执行,那么首先思考两个问题:

  1. 如果有多个defer,执行顺序是怎样的?
  2. defer是在return之前还是之后执行的?如果defer执行的内容对返回值进行了修改,return结果是否会改变?

多个defer的执行顺序

通过下面这个个例子,我们看下defer的执行顺序:

package main

import (
    "fmt"
)

func main() {
    defer fmt.Println("main defer1")
    test()
    defer fmt.Println("main defer2")
}

func test() () {
    defer func() {
        fmt.Println("test defer1")
    }()
    defer func() {
        fmt.Println("test defer2")
    }()
}

执行结果为:

test defer2
test defer1
main defer2
main defer1

我们发现defer就像一个LIFO的栈,后defer的会先执行,且在函数退出时才会执行。

defer与return的执行顺序

首先看个例子:

package main

import (
    "fmt"
)

func main() {
    ret := test()
    fmt.Println("test return:", ret)
}

func test() ( int) {
    var i int

    defer func() {
        i++        //defer里面对i增1
        fmt.Println("test defer, i = ", i)
    }()

    return i
}

执行结果为:

test defer, i =  1
test return: 0

test函数的返回值为0,defer里面的i++操作好像对返回值并没有什么影响。
这是否表示“return i”执行结束以后才执行defer呢?
非也!再看下面的例子:

package main

import (
    "fmt"
)

func main() {
    ret := test()
    fmt.Println("test return:", ret)
}

//返回值改为命名返回值
func test() (i int) {
    //var i int

    defer func() {
        i++
        fmt.Println("test defer, i = ", i)
    }()

    return i
}

执行结果为:

test defer, i =  1
test return: 1

这次test函数的返回值变成了1,defer里面的“i++"修改了返回值。所以defer的执行时机应该是return之后,且返回值返回给调用方之前。
至于第一个例子中test函数返回值不是1的原因,还涉及到函数匿名返回值与命名返回值的差异,以后再单独分析。

结论

  1. defer的执行顺序为:后defer的先执行。
  2. defer的执行顺序在return之后,但是在返回值返回给调用方之前,所以使用defer可以达到修改返回值的目的。