defer推遲執行
func main() {
// 在打開資源後關閉的時候通常會用到defer,例如關閉打開的文件它能確保你不會忘記關閉文件。
readFile()
// 被推遲執行的函數,如果有實參傳入該函數,那麼該實參會在**推遲執行**的時候就會計算,而不是在**實際執行**的時候計算,例如:
calcA()
// defer的執行順序是LIFO 先入後出
calcB()
// defer 與return的執行順序,輸出結果爲0 1,可以看出先執行return後面的方法,再執行的defer後面的方法
// defer、return、返回值三者的執行邏輯應該是:return最先執行,return負責將結果寫入返回值中;接着defer開始執行一些收尾工作;最後函數攜帶當前返回值退出。
deferReturn(0)
// defer 推遲執行的方法,實參是指針,改變參數的值會影響defer後面函數的執行,因爲defer的時候實參實際上是一個指針地址
a := &ask{a: 1}
deferReturn2(a)
1.defer常用來關閉打開的資源
// readFile defer常用來關閉打開的文件
func readFile() error {
f, err := os.Open("README.md")
if err != nil {
return err
}
defer f.Close()
content, err := ioutil.ReadAll(f)
if err != nil {
return err
}
fmt.Printf("%s", content)
return nil
}
2.defer推遲執行的函數的實參
// calcA 被推遲函數的實參(如果該函數爲方法則還包括接收者)在推遲執行時就會求值, 而不是在調用執行時才求值。
func calcA() {
a, b := 1, 2
defer fmt.Println(sum(a, b))
defer fmt.Println(a)
a, b = 3, 4
fmt.Println(sum(a, b))
}
3.defer的調用順序
// calcB 被推遲的函數按照後進先出(LIFO)的順序執行。所以會輸出結果是 7 3
func calcB() {
a, b := 1, 2
defer fmt.Println(sum(a, b))
c, d := 3, 4
defer fmt.Println(sum(c, d))
}
func sum(a, b int) int {
return a + b
}
4.defer和return的執行順序
func printA(a int) int {
fmt.Println(a)
return a
}
// deferReturn defer和return的執行順序,輸出結果爲0 1,可以看出先執行return後面的方法,再執行的defer後面的方法
func deferReturn(a int) int {
defer printA(a + 1)
return printA(a)
}
5.defer推遲執行的函數參數爲指針類型時
type ask struct {
a int
}
func printAsk(a *ask) {
fmt.Println(a.a)
}
// deferReturn2 輸出結果是1 10,說明defer推遲執行的方法的參數如果是一個指針類型,在defer之後對參數值進行修改,會影響到defer後面的方法實際執行時參數的內容
func deferReturn2(a *ask) {
printAsk(a)
defer printAsk(a)
a.a = 10
}
6.defer函數執行的值有可能會影響return的值
https://my.oschina.net/henrylee2cn/blog/505535
7.defer的作用域
- defer只對當前協程有效(main可以看作是主協程);
- 當panic發生時依然會執行當前(主)協程中已聲明的defer,但如果所有defer都未調用recover()進行異常恢復,則會在執行完所有defer後引發整個進程崩潰;
- 主動調用os.Exit(int)退出進程時,已聲明的defer將不再被執行。