官方文檔中關於defer語句的解釋:
defer語句順延強制一個函數,該函數被延遲到當包含它的程式返回時(包含它的函數 執行了return語句/運行到函數結尾自動返回/對應的goroutine panic)執行。
每次defer語句執行時,defer修飾的函數的傳回值和參數取值會照常進行計算和儲存,但是該函數不會執行。等到上一級函數返回前,會按照defer的聲明順序倒序執行全部defer的函數。defer的函數的任何傳回值都會被丟棄。
如果一個defer的函數值為nil,則這個defer的函數會在函數執行時panic(異常),而不會在defer語句執行時panic,例如:
func test() { f := func(){ fmt.Println("Test") } defer f() f = nil // 這裡將函數的“值”設為nil,此時再嘗試執行這個函數的時候 defer f() defer fmt.Println("Test1") } func main() { test() } |
執行結果:
Test1 Test panic: runtime error: invalid memory address or nil pointer dereference [signal 0xc0000005 code=0x0 addr=0x0 pc=0x451668] goroutine 1 [running]: panic(0x49aaa0, 0xc042004090) D:/Program Files/Go/src/runtime/panic.go:500 +0x1af main.test() D:/Programming/GoWork/defer.go:28 +0xfc main.main() D:/Programming/GoWork/defer.go:31 +0x1b |
可以看到,第一個Test1正常輸出了,說明設定f = nil後,defer f()並未直接panic,而是在test()返回之前,倒序執行到第二次f()的時候,拋出異常。同時Test也輸出出來了,說明defer的方法在會在panic之前執行,不會因為panic了而停止執行,對比測試:
func test() { f := func(){ fmt.Println("Test") } f() f = nil f() fmt.Println("Test1") } func main() { test() } |
執行結果:
Test panic: runtime error: invalid memory address or nil pointer dereference [signal 0xc0000005 code=0x0 addr=0x0 pc=0x40107a] goroutine 1 [running]: panic(0x49aa20, 0xc042004090) D:/Program Files/Go/src/runtime/panic.go:500 +0x1af main.test() D:/Programming/GoWork/defer.go:26 +0x3a main.main() D:/Programming/GoWork/defer.go:31 +0x1b exit status 2 |
可以看到Test1並未輸出,原因是在執行fmt.Println("Test1")之前test()已經panic了。
插播一條panic的處理方法:
通過內建方法recover()可以捕獲panic,recover()方法有一個傳回值,如果recover()捕獲到panic,則返回panic對象,否則返回nil。下面是官方文檔中的一個recover()的例子,封裝了一個方法來捕獲panic(類似於try…catch…):
func test() { f := func(){ fmt.Println("Test") } f() f = nil f() fmt.Println("Test1") } func safeFunc(target func()) { defer func() { if x := recover(); x != nil { fmt.Println("Runtime panic: ", x) } } () target() } func main() { safeFunc(test) } |
執行結果:
Test Runtime panic: runtime error: invalid memory address or nil pointer dereference |
可以使用panic()函數人工拋出一個panic:
panic("Error Message")
回到defer的用法,下面舉了幾個例子來說明defer的用法:
樣本1:
func main() { for i := 0; i < 3; i++ { defer fmt.Println(i) } } |
輸出:2 1 0
上面的代碼等價於:
func main() { defer fmt.Println(0) // 每次defer語句執行時,defer修飾的函數的傳回值和參數取值會照常進行計算和儲存 defer fmt.Println(1) // 同理 defer fmt.Println(2) // 同理 } |
即:
func main() { fmt.Println(2) fmt.Println(1) fmt.Println(0) } |
樣本2:
func main() { for i := 0; i < 3; i++ { defer func() { fmt.Println(i) } () } } |
輸出:
3 3 3
上面的代碼等價於:
func main() { i := 0 defer func() { fmt.Println(i) } () // defer修飾的函數是閉包函數,而非裡面的fmt語句,故裡面的參數不變 i++ // i = 1 defer func() { fmt.Println(i) } () i++ // i = 2 defer func() { fmt.Println(i) } () i++ // i = 3 } |
即:
func main() { i := 0 i++ i++ i++ func() { fmt.Println(i) } () func() { fmt.Println(i) } () func() { fmt.Println(i) } () } |
樣本3: