go-defer
[TOC]
源码
runtime/runtime2.go
defer数据结构:
可以看到defer是一个链表,定义多个defer会组织成一条链依次调用:
预计算参数
defer后面跟的是一个函数,如果是一个正常函数,根据golang值传递原理,在定义defer时值就会被拷贝,因此虽然函数是延时调用的,但是值却是实时赋值。 如果想实现值的延时赋值,则需要为defer传递一个闭包,闭包的原理就是一个函数指针,那么传递给defer的是指针拷贝,而不是函数拷贝,就能实现值的延时赋值。
编译
编译源码:
从这里看到,编译器把defer当做了函数调用,因此defer fmt.Println(1)
本质上如同defer(fmt.Println(1))
,这里就解释了预计算参数的问题,defer是函数,那么后面的函数就是参数了。
defer执行
defer是在return之前执行的。这个在 官方文档中是明确说明了的。要使用defer时不踩坑,最重要的一点就是要明白,return xxx这一条语句并不是一条原子指令! 函数返回的过程是这样的:先给返回值赋值,然后调用defer表达式,最后才是返回到调用函数中。 defer表达式可能会在设置函数返回值之后,在返回到调用函数之前,修改返回值,使最终的函数返回值与你想象的不一致。 其实使用defer时,用一个简单的转换规则改写一下,就不会迷糊了。改写规则是将return语句拆成两句写,return xxx会被改写成:
最后更新于