前言
大家都知道Go编程中,假设在函数F里,执行了defer A(),那在函数F正常return之前或者因为panic要结束运行之前,被defer关键字修饰的函数调用A()都会被执行到。
比如下面的2个例子:
test1()会在main结束之前执行
1 | go复制代码// defer1.go |
这个例子输出的结果是:
1 | go复制代码main start |
test1()会在panic之前执行
1 | go复制代码// defer2.go |
这个例子输出的结果是:
1 | go复制代码main start |
问题
如果在函数F里,defer A()这个语句执行了,是否意味着A()这个函数调用一定会执行?
这里大家可以先脑补一会。
请看下面的例子:
1 | go复制代码// defer3.go |
上面的代码运行结果会是怎么样?
结论
上面defer3.go执行的结果是:
1 | go复制代码main start |
被defer的test1()并没有在main结束之前执行。这是为什么呢?
查看os.Exit的说明如下:
1 | go复制代码Exit causes the current program to exit with the given status code. Conventionally, code zero indicates success, non-zero an error. The program terminates immediately; deferred functions are not run. |
如果在函数里是因为执行了os.Exit而退出,而不是正常return退出或者panic退出,那程序会立即停止,被defer的函数调用不会执行。
defer 4原则回顾
- defer后面跟的必须是函数或者方法调用,defer后面的表达式不能加括号。
1 | go复制代码defer (fmt.Println(1)) // 编译报错,因为defer后面跟的表达式不能加括号 |
- 被defer的函数的参数在执行到defer语句的时候就被确定下来了。
1 | go复制代码func a() { |
上例中,被defer的函数fmt.Println的参数i在执行到defer这一行的时候,i的值是0,fmt.Println的参数就被确定下来是0了,因此最终打印的结果是0,而不是1。
3. 被defer的函数执行顺序满足LIFO原则,后defer的先执行。
1 | go复制代码func b() { |
上例中,输出的结果是3210,后defer的先执行。
4. 被defer的函数可以对defer语句所在的函数的命名返回值做读取和修改操作。
1 | go复制代码// f returns 42 |
上例中,被defer的函数func对defer语句所在的函数f的命名返回值result做了修改操作。
调用函数f,返回的结果是42。
执行顺序是函数f先把要返回的值6赋值给result,然后执行被defer的函数func,result被修改为42,然后函数f返回result,也就是返回了42。
官方说明如下:
1 | go复制代码Each time a "defer" statement executes, the function value and parameters to |
代码
相关代码和说明开源在GitHub:github.com/jincheng9/g…
也可以搜索公众号:coding进阶,查看更多Go知识。
一起进步!
本文转载自: 掘金