什么是defer?
defer陳述句是專門在函式結束以后做一些清理作業的,我們先舉一個例子來更好的理解,現在有一個函式,它的作用是把一個檔案內容拷貝到另一個檔案,
func CopyFile(dstName string, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
dst, err := os.Create(dstName)
if err != nil {
return
}
written, err = io.Copy(dst, src)
src.Close()
dst.Close()
return
}
以上代碼是可以正常執行的,但是存在一個問題,如果os.Create執行失敗,那么就無法執行到檔案資源的Close函式,行程每打開一個檔案就會占用一個檔案描述符,而在系統當中,檔案描述符是有上限的,可以通過ulimit -n查看,如果資源沒有被及時釋放,會出現資源浪費的情況,如果打開檔案過多,也會出現Too many open files的提示,這個時候就需要通過defer來解決問題了,代碼如下,
func CopyFile(dstName string, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
defer src.Close()
dst, err := os.Create(dstName)
if err != nil {
return
}
defer dst.Close()
written, err = io.Copy(dst, src)
return
}
defer陳述句會在return引數設定之后、函式回傳給呼叫者之前執行,這樣就不再擔心檔案資源無法被Close了,
defer的三個規則
規則一:被deferred的函式引數在defer時確定
func a() {
i := 0
defer fmt.Println(i)
i++
return
}
我們通過以上代碼來理解這個拗口的規則,如果根據官方的定義來理解這段代碼,變數i的值鐵定為1,但實際執行的結果不是1,卻是0,
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.
那我們再重新來理解這個規則,變數i是在逐行執行到defer陳述句的時候就已經確定了值,這個時候變數i還沒有進行自增,所以輸出的結果應該是0而不是1,
規則二:被deferred函式執行順序遵循LIFO原則
func b() {
for i := 0; i < 4; i++ {
defer fmt.Print(i)
}
}
LIFO全稱為Last In First Out,意為后進先出,堆疊是一種典型的LIFO資料結構,defer也是如此,拿以上代碼為例,先后遍歷了四次,也就是做了四次壓堆疊操作,同理,在函式return之前,就會逐一出堆疊,倒序執行defer陳述句,所以上述代碼輸出內容為3210,

規則三:deferred函式可以讀取和修改函式的回傳值
func c() (i int) {
defer func() { i++ }()
return 1
}
我們定義一個defer函式,將變數i自增,那么最終變數i的值為2,我們從官方檔案中可以看出,defer陳述句是在函式設定回傳值后,且在回傳給主調函式前執行的,根據這個思路,c函式已經return了1,這個時候執行了defer函式,將回傳的結果1進行了自增,然后回傳給主調函式,這個時候主調函式拿到的值就是2了,
deferred functions are executed after any result parameters are set by that return statement but before the function returns to its caller
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/33543.html
標籤:Go
上一篇:Golang中的內置函式
下一篇:理解Go語言組件flag
