defer使用
defer是Go語(yǔ)言提供的一種用于注冊(cè)延遲調(diào)用的機(jī)制:讓函數(shù)或語(yǔ)句可以在當(dāng)前函數(shù)執(zhí)行完畢后(包括通過(guò)return正常結(jié)束或者panic導(dǎo)致的異常結(jié)束)執(zhí)行锌蓄。
func main() {
defer_call()
}
func defer_call() {
defer func() { fmt.Println("打印前") }()
defer func() { fmt.Println("打印中") }()
defer func() { fmt.Println("打印后") }()
panic("觸發(fā)異常")
}
輸出
打印后
打印中
打印前
panic: 觸發(fā)異常
goroutine 1 [running]:
main.defer_call()
D:/GoProjs/interview/ts01/main.go:15 +0x98
main.main()
D:/GoProjs/interview/ts01/main.go:8 +0x27
exit status 2
在Panic觸發(fā)時(shí)結(jié)束函數(shù)運(yùn)行驾荣,在return前先依次打印:打印后、打印中帆吻、打印前域那。最后由runtime運(yùn)行時(shí)拋出打印panic異常信息。需要注意的是猜煮,函數(shù)的return value 不是原子操作次员,而是在編譯器中分解為兩部分:返回值賦值和return败许。而defer剛好被插入到末尾的return前執(zhí)行。故可以在derfer函數(shù)中修改返回值淑蔚。
defer拆解
return xxx
上面的return拆解如下
1. 返回值=xxx
2. 調(diào)用defer函數(shù)
3. 空的return
1市殷,3步驟才是return語(yǔ)句真正的命令
func main() {
fmt.Println(doubleScore(0)) //0
fmt.Println(doubleScore(20.0)) //40
fmt.Println(doubleScore(50.0)) //50
}
func doubleScore(s float32) (score float32) {
defer func() {
if score < 1 || score >= 100 {
//將影響返回值
score = s
}
}()
score = s * 2
return
//或者
//return source * 2
}
底層原理
每次defer語(yǔ)句執(zhí)行的時(shí)候,會(huì)把函數(shù)“壓検叮”被丧,函數(shù)參數(shù)會(huì)被拷貝下來(lái)。defer語(yǔ)句并不會(huì)馬上執(zhí)行绪妹,而是會(huì)進(jìn)入一個(gè)棧甥桂,函數(shù)return前,會(huì)按先進(jìn)后出的順序執(zhí)行邮旷。也說(shuō)是說(shuō)最先被定義的defer語(yǔ)句最后執(zhí)行黄选。先進(jìn)后出的原因是后面定義的函數(shù)可能會(huì)依賴前面的資源,自然要先執(zhí)行婶肩;否則办陷,如果前面先執(zhí)行,那后面函數(shù)的依賴就沒(méi)有了律歼。如果defer執(zhí)行的函數(shù)為nil, 那么會(huì)在最終調(diào)用函數(shù)的產(chǎn)生panic
在defer函數(shù)定義時(shí)民镜,對(duì)外部變量的引用是有兩種方式的,分別是作為函數(shù)參數(shù)和作為閉包引用险毁。作為函數(shù)參數(shù)制圈,則在defer定義時(shí)就把值傳遞給defer,并被cache起來(lái)畔况;作為閉包引用的話鲸鹦,則會(huì)在defer函數(shù)真正調(diào)用時(shí)根據(jù)整個(gè)上下文確定當(dāng)前的值。
defer后面的語(yǔ)句在執(zhí)行的時(shí)候跷跪,函數(shù)調(diào)用的參數(shù)會(huì)被保存起來(lái)馋嗜,也就是復(fù)制了一份。真正執(zhí)行的時(shí)候吵瞻,實(shí)際上用到的是這個(gè)復(fù)制的變量葛菇,因此如果此變量是一個(gè)“值”,那么就和定義的時(shí)候是一致的橡羞。如果此變量是一個(gè)“引用”熟呛,那么就可能和定義的時(shí)候不一致。
exp1
func main() {
s := [3]int{1, 2, 3}
for i := 0; i < 2; i++ {
defer func() {
fmt.Println(s[i])
}()
}
}
輸出
3
3
// for循環(huán)完畢后才開(kāi)始執(zhí)行defer函數(shù)尉姨,而此時(shí)的i值為2
exp2
type number int
func (n number) print() { fmt.Println(n) }
func (n *number) pprint() { fmt.Println(*n) }
func main() {
var n number
defer n.print()
defer n.pprint()
defer func() { n.print() }()
defer func() { n.pprint() }()
n = 3
}
輸出
3
3
3
0
//第四個(gè)defer語(yǔ)句是閉包庵朝,引用外部函數(shù)的n, 最終結(jié)果是3;
第三個(gè)defer語(yǔ)句同第四個(gè);
第二個(gè)defer語(yǔ)句,n是引用九府,最終求值是3.
第一個(gè)defer語(yǔ)句椎瘟,對(duì)n直接求值,開(kāi)始的時(shí)候n=0, 所以最后是0;
exp3
func calc(index string, a, b int) int {
ret := a + b
fmt.Println(index, a, b, ret)
return ret
}
func main() {
a := 1
b := 2
defer calc("1", a, calc("10", a, b))
a = 0
defer calc("2", a, calc("20", a, b))
b = 1
}
輸出
10 1 2 3
20 0 2 2
2 0 2 2
1 1 3 4
注意
package main
import "fmt"
func main() {
fmt.Println(getScore()) // 100
fmt.Println(getScoreV2()) // 25.5
}
func getScore() float64 {
var score float64
defer func() {
score = 25.5
}()
score = 100
return score
}
func getScoreV2() (score float64) {
defer func() {
score = 25.5
}()
score = 100
return score
}