參考文章:深入理解Go語言
defer用于資源的釋放哑梳,會在函數(shù)返回之前進(jìn)行調(diào)用
defer關(guān)鍵字的實(shí)現(xiàn)跟go關(guān)鍵字很類似丁频,不同的是它調(diào)用的是runtime.deferproc而不是runtime.newproc。
在defer出現(xiàn)的地方期犬,插入了指令call runtime.deferproc敞斋,然后在函數(shù)返回之前的地方丹墨,插入指令call runtime.deferreturn。
普通的函數(shù)返回時(shí)龙巨,匯編代碼類似:
add xx SP
return
如果其中包含了defer語句笼呆,則匯編代碼是:
call runtime.deferreturn,
add xx SP
return
goroutine的控制結(jié)構(gòu)中旨别,有一張表記錄defer诗赌,調(diào)用runtime.deferproc時(shí)會將需要defer的表達(dá)式記錄在表中,而在調(diào)用runtime.deferreturn的時(shí)候秸弛,則會依次從defer表中出棧并執(zhí)行铭若。
實(shí)例一:先按照普通函數(shù)執(zhí)行洪碳,然后再按照defer 的先進(jìn)后出逆向執(zhí)行
func main() {
fmt.Println("hello world 123")
defer goodBye()
defer goodNight()
goodEating()
fmt.Println("hello world")
}
func goodNight() {
fmt.Println("goodnight")
}
func goodBye() {
fmt.Println("goodbye")
}
func goodEating() {
fmt.Println("goodeating")
}
---------output-----------
hello world 123
goodeating
hello world
goodnight
goodbye
實(shí)例二:若在中間添加一個(gè) return,那么在return之上的叼屠,先按照普通函數(shù)執(zhí)行瞳腌,然后按照defer 的先進(jìn)后出逆向執(zhí)行,在return之下的不執(zhí)行
func main() {
fmt.Println("hello world 123")
defer goodBye()
defer goodNight()
goodEating()
return
fmt.Println("hello world")
}
func goodNight() {
fmt.Println("goodnight")
}
func goodBye() {
fmt.Println("goodbye")
}
func goodEating() {
fmt.Println("goodeating")
}
------output-------
hello world 123
goodeating
goodnight
goodbye
實(shí)例三:返回值操作
func f() (result int) {
defer func() {
result++
}()
return 0
}
由于 return xxx這一條語句并不是一條原子指令!
所以 return xxx 分為 三步操作 :返回值 = xxx 镜雨,調(diào)用defer函數(shù)嫂侍,空的return,上面代碼可以看作下面的代碼
func f() (result int) {
result = 0 //return語句不是一條原子調(diào)用荚坞,return xxx其實(shí)是賦值+ret指令
func() { //defer被插入到return之前執(zhí)行挑宠,也就是賦返回值和ret指令之間
result++
}()
return
}
實(shí)例四:帶參數(shù)的賦值返回值操作
func f() (r int) {
t := 5
defer func() {
t = t + 5
}()
return t
}
上述的return返回的是 t,而我們func的返回值是 r颓影,上面代碼可以看作下面的代碼
func f() (r int) {
t := 5
r = t //賦值指令
func() { //defer被插入到賦值與返回之間執(zhí)行各淀,這個(gè)例子中返回值r沒被修改過
t = t + 5
}
return //空的return指令
}
實(shí)例五
func f() (r int) {
defer func(r int) {
r = r + 5
}(r)
return 1
}
分析:
func f() (r int) {
r = 1 //給返回值賦值
func(r int) { //這里改的r是傳值傳進(jìn)去的r,不會改變要返回的那個(gè)r值
r = r + 5
}(r)
return //空的return
}
實(shí)例六
func main() {
i := deferRet(1,1)
println(i) // print 152
}
func deferRet(x,y int) (z int){
defer func () { z += 100 }()
z = x + y
return z + 50 // 執(zhí)行順序 z = z+50 -> (call defer)z = z+100 -> ret
}
分析:
func deferRet(x,y int) (z int){
z = x + y // z=2
z = z+50 //給返回值賦值z=52
defer z += 100 //z = 150
return
}
本質(zhì)原因是return xxx語句并不是一條原子指令诡挂,defer被插入到了賦值 與 ret之間碎浇,因此可能有機(jī)會改變最終的返回值。