去年輸出了一系列g(shù)olang的編碼文章,但總感覺有話沒講,但又不清楚具體是什么,所以本文以隨筆為主。
我們知道函數(shù)的調(diào)用其實(shí)就是一個(gè)入棧和出棧的動(dòng)作:
main() --> normal()
試想當(dāng)執(zhí)行函數(shù)normal()時(shí)出現(xiàn)異常時(shí),會(huì)有什么情況發(fā)生呢?
package main
import (
? ? "fmt"
)
func normal(a int) int {
? ? ? ??var b int = 0
? ? ? ? return a / b
}
func main() {
? ? ? ? var a int =
? ? ? ? fmt.Println("a/b= ", normal(a))
}
你肯定想,這會(huì)拋錯(cuò)呀~
是的,U are right,會(huì)拋出panic: runtime error: integer divide by zero.
進(jìn)一步想,如果var b int = 0不是簡(jiǎn)單的賦值,而是一塊內(nèi)存的分配,不幸的是,剛分配完內(nèi)存就拋異常了,那么該內(nèi)存就永遠(yuǎn)沒有被釋放的機(jī)會(huì)。
如何解決?其它語言有:try、catch、finally等關(guān)鍵字。
golang采用了defer關(guān)鍵字,該關(guān)鍵字用于告訴程序:“wait,wait,我做點(diǎn)事情之后,你再退出本次調(diào)用”。
func normal(a int) int {
? ? ? ??defer fmt.Println("wait, wait for me.")
? ? ? ? var b int = 0
? ? ? ? return a / b
}
修改normal()函數(shù),在函數(shù)體內(nèi)增加defer再運(yùn)行,就會(huì)發(fā)現(xiàn)在拋異常之前把“wait, wait for me.”打印出來了。
這表示在函數(shù)normal()被調(diào)用之后,/0遇到了問題,此時(shí)golang會(huì)拋出panic前,defer說等等,等我打印點(diǎn)東西后,你再拋。
【defer語法】:
defer 表達(dá)式
func normal(a int) int {
? ? ? ??defer?func() {
? ? ? ? ? ? ? ? ?fmt.Println("panic will be throwen.")
? ? ? ??}()
? ? ? ? var b int = 0
? ? ? ? return a / b
}
當(dāng)表達(dá)式是一個(gè)匿名函數(shù)時(shí),一定要記得后面追加(),這表示是一個(gè)表達(dá)式 :)
【defer使用場(chǎng)景】:
defer一般使用在函數(shù)體開始,或者緊跟著申請(qǐng)資源的語句后面
不建議把defer放到函數(shù)體的后面。修改一下上面的示例:
func normal(a int) {
var b int = 0
fmt.Println("a/b= ", a/b)
defer?func() {
fmt.Println("panic will be throwen.")
}()
}
func main() {
var a int =
normal(a)
}
此時(shí)的defer已無意義,所以"panic will be throwen."不會(huì)被打印出來。
【多個(gè)順序defer】:
被調(diào)用函數(shù)中若有多個(gè)順序defer,則先會(huì)出現(xiàn)“先定義后執(zhí)行”現(xiàn)象
func main() {
defer fmt.Println("0")
defer fmt.Println("1")
defer fmt.Println("2")
defer fmt.Println("3")
defer fmt.Println("4")
fmt.Println("Test multi defers")
}
執(zhí)行結(jié)果為:
Test multi defers
4
3
2
1
0
想想這很自然,從堆棧來看,越是后面定義的defer越是處于堆棧的棧頂。
該代碼可以精簡(jiǎn)為:
func main() {
? ? ? ??for i := 0; i < 5; i++ {
? ? ? ? ? ? ? defer fmt.Println(i)
? ? ? ? }
? ? ? ? fmt.Println("Test multi defers")
}
【defer表達(dá)式中存在函數(shù)調(diào)用】:
defer語句被執(zhí)行的時(shí)候,傳遞給延遲函數(shù)的參數(shù)都會(huì)被求值,但是延遲函數(shù)調(diào)用表達(dá)式并不會(huì)在此時(shí)被求值。
感覺這句話比較繞口,不好難理解?先看一個(gè)例子:
func history(date string) string { ?// 打印"2016 will be history",并返回"2017"字符串
s := date + " will be history."
fmt.Println(s)
return "2017"
}
func future(date string) string { ?// 打印"2017 will be coming",并返回"2017 will be coming"字符串
s := date + " will be coming."
fmt.Println(s)
return s
}
func main() {
defer future(history("2016"))
fmt.Println("It's the Spring Festival now.")
}
對(duì)照著defer future(history("2016"))理解一下“傳遞給延遲函數(shù)的參數(shù)都會(huì)被求值,但是延遲函數(shù)調(diào)用表達(dá)式并不會(huì)在此時(shí)被求值”。
延遲函數(shù):future()
延遲函數(shù)的參數(shù):history("2016")
由于延遲函數(shù)的參數(shù)會(huì)被求值,即history("2016")會(huì)被執(zhí)行,所以會(huì)先指印出“2016 will be history”,同時(shí)延遲函數(shù)變?yōu)閒uture("2017"),它要求被延遲執(zhí)行。
從而該程序執(zhí)行結(jié)果為:
2016 will be history.
It's the Spring Festival now.
2017 will be coming.
感覺“defer語句被執(zhí)行的時(shí)候,傳遞給延遲函數(shù)的參數(shù)都會(huì)被求值,但是延遲函數(shù)調(diào)用表達(dá)式并不會(huì)在此時(shí)被求值”這語句已經(jīng)理解了,請(qǐng)?jiān)倏聪旅娴睦樱?/span>
func main() {
for i := 0; i < 5; i++ {
defer func() {
fmt.Println(i)
}()
}
fmt.Println("Test multi defers")
}
它的運(yùn)行結(jié)果為:
Test multi defers
5
5
5
5
5
是不是有點(diǎn)懵逼了?
對(duì)照著defer func(){
? ? ? ? ? ? ? ? ? ? fmt.Println(i)
? ? ? ? ? }()
理解一下“傳遞給延遲函數(shù)的參數(shù)都會(huì)被求值,但是延遲函數(shù)調(diào)用表達(dá)式并不會(huì)在此時(shí)被求值”
延遲函數(shù)表達(dá)式:
? ? ? ?func() {
? ? ? ? ? ? ? ?fmt.Println(i)
? ? ? ?}()
在defer語句被執(zhí)行時(shí),該表達(dá)式并不會(huì)被求值,即被執(zhí)行,i值你自己玩吧,所以等循環(huán)完成之后i值變?yōu)?,再打印出“Test multi defers”,函數(shù)馬上要return時(shí),這5個(gè)defer分別說:“wait, wait for me.”。
于是第5個(gè)defer表達(dá)式被執(zhí)行,打印i值(這時(shí)i值為5),所以打印出:
5
于是第4個(gè)defer表達(dá)式被執(zhí)行,打印i值(這時(shí)i值為5),所以打印出:
5
于是第3個(gè)defer表達(dá)式被執(zhí)行,打印i值(這時(shí)i值為5),所以打印出:
5
于是第2個(gè)defer表達(dá)式被執(zhí)行,打印i值(這時(shí)i值為5),所以打印出:
5
于是第1個(gè)defer表達(dá)式被執(zhí)行,打印i值(這時(shí)i值為5),所以打印出:
5
從而出現(xiàn)該結(jié)果 :)
接下來咋玩?
func main() {
for i := 0; i < 5; i++ {
defer func(n int) {
fmt.Println(n)
}(i)
}
fmt.Println("Test multi defers")
}
再理解"defer語句被執(zhí)行的時(shí)候,傳遞給延遲函數(shù)的參數(shù)都會(huì)被求值,但是延遲函數(shù)調(diào)用表達(dá)式并不會(huì)在此時(shí)被求值"一下:
當(dāng)i=0時(shí)
延遲函數(shù)調(diào)用表達(dá)式:func(n int) { fmt.Println(n) }(i)
延遲函數(shù)的參數(shù):n
延遲函數(shù)調(diào)用表達(dá)式不會(huì)被求值,但延遲函數(shù)的參數(shù)i會(huì)被求值,所以n值變?yōu)?
當(dāng)i=1時(shí)
延遲函數(shù)調(diào)用表達(dá)式:func(n int) { fmt.Println(n) }(i)
延遲函數(shù)的參數(shù):n
延遲函數(shù)調(diào)用表達(dá)式不會(huì)被求值,但延遲函數(shù)的參數(shù)i會(huì)被求值,所以n值變?yōu)?
依次類推,從而最終執(zhí)行結(jié)果為:
Test multi defers
4
3
2
1
0
defer好玩吧,上面的例子有的來源自于網(wǎng)絡(luò)上其他人的博客。
因篇幅問題不能全部顯示,請(qǐng)點(diǎn)此查看更多更全內(nèi)容
Copyright ? 2019- 91gzw.com 版權(quán)所有 湘ICP備2023023988號(hào)-2
違法及侵權(quán)請(qǐng)聯(lián)系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市萬商天勤律師事務(wù)所王興未律師提供法律服務(wù)