什么是defer?
defer
是go語言提供的一種用于注冊延遲調(diào)用的機制悦污,讓函數(shù)或語句可以在當前函數(shù)執(zhí)行完畢后(包括通過return正常結(jié)束或panic導致的異常結(jié)束)執(zhí)行抗悍。
適用場景:
- 打開/關(guān)閉連接
- 加鎖/釋放鎖
- 打開/關(guān)閉文件等
defer
在一些需要回收資源的場景非常有用,可以很方便在函數(shù)結(jié)束前做一些清理工作搏存。
為什么要用defer秫舌?
在編程過程中的妖,經(jīng)常需要打開一些資源,比如數(shù)據(jù)庫足陨、文件嫂粟、鎖等,這些資源都需要用完釋放墨缘,否則會造成內(nèi)存泄漏星虹。
當然在使用過程中零抬,可以在函數(shù)結(jié)束時顯式關(guān)閉資源,但是如果在打開和關(guān)閉資源之間如果發(fā)生了panic會退出函數(shù)宽涌,導致關(guān)閉資源沒有被執(zhí)行平夜。因為這樣一顆語法糖,減少了很多資源泄漏的情況卸亮。
defer底層
官方對defer
的解釋:
Each time a “defer” statement executes, the function value and parameters to the call are evaluated as usual and saved anew but the actual function is not invoked. Instead, deferred functions are invoked immediately before the surrounding function returns, in the reverse order they were deferred. If a deferred function value evaluates to nil, execution panics when the function is invoked, not when the “defer” statement is executed.
每次defer
語句執(zhí)行時忽妒,會把函數(shù)“壓棧”嫡良,函數(shù)的參數(shù)會被拷貝下來锰扶,當外層函數(shù)退出時献酗,defer函數(shù)按照定義的逆序執(zhí)行寝受,如果defer執(zhí)行的函數(shù)為nil,那么會在最終調(diào)用函數(shù)產(chǎn)生panic罕偎。
這里有一道經(jīng)典題:
func main() {
a,b := 1,2
defer cal("1",a,cal("10",a,b))
a = 0
defer cal("2",a,cal("20",a,b))
}
func cal(index string, a, b int) int {
ret := a + b
fmt.Println(index,a,b,ret)
return ret
}
// Output:
10 1 2 3
20 0 2 2
2 0 2 2
1 1 3 4
這是遵循先入后出的原則很澄,同時保留當前變量的值。
看看下面這道題:
func f1() (r int) {
defer func() {
r++
}
return 0
}
func f2() (r int) {
t := 5
defer func() {
t = t + 5
}
return t
}
func f3() (r int) {
defer func(r int) {
r = r + 5
}(r)
return 1
}
// Output:
1
5
1
你能正確答對嗎颜及?
關(guān)鍵點在于理解這條語句:
return xxx
這條語句并不是一個原子命令甩苛,經(jīng)過編譯后,變成3條指令:
1俏站、返回值=xxx
2讯蒲、調(diào)用defer函數(shù)
3、空的return
那么我們來拆解上面3個函數(shù)肄扎。
func f1() (r int) {
// 1墨林、賦值
r = 0
// 2、閉包引用
defer func() {
r++
}
// 3犯祠、空的return
return 0
}
// defer是閉包引用旭等,所以返回值被修改,所以f1()返回1
func f2() (r int) {
t := 5
// 1衡载、賦值
r = t
// 2搔耕、閉包引用,但沒有修改r
defer func() {
t = t + 5
}
// 3痰娱、空的return
return t
}
// 沒涉及返回值r的操作弃榨,所以返回5
func f3() (r int) {
// 1、賦值
r = 1
// 2梨睁、r作為參數(shù)傳值惭墓,不會修改返回值的r
defer func(r int) {
r = r + 5
}(r)
// 3、空的return
return
}
// 第二步r是作為函數(shù)參數(shù)使用的而姐,是一份復制腊凶,defer語句中的r和外面的r是兩個變量,里面r的變化不會改變外面r,所以返回1.