什么是defer
defer語(yǔ)句用于golang程序中延遲函數(shù)的調(diào)用, 每次defer都會(huì)把一個(gè)函數(shù)壓入棧中, 函數(shù)返回前再把延遲的函數(shù)取出并執(zhí)行。
為了方便描述, 我們把創(chuàng)建defer的函數(shù)稱為主函數(shù), defer語(yǔ)句后面的函數(shù)稱為延遲函數(shù)。延遲函數(shù)可能有輸入?yún)?shù), 這些參數(shù)可能來(lái)源于定義defer的函數(shù), 延遲函數(shù)也可能引用主函數(shù)用于返回的變量, 也就是說(shuō)延遲函數(shù)可能會(huì)影響主函數(shù)的一些行為。
defer的規(guī)則
規(guī)則一:延遲函數(shù)的參數(shù)在defer語(yǔ)句出現(xiàn)時(shí)就已經(jīng)確定
package main
import "fmt"
func main() {
deferFuncParameter()
}
func deferFuncParameter() {
var aInt = 1
defer fmt.Println(aInt)
aInt = 2
return
}
結(jié)果:
代碼說(shuō)明: 函數(shù)deferFuncParameter()定義一個(gè)整型變量并初始化為1,然后使用defer語(yǔ)句打印出變量值, 最后修改變量值為2。
參考答案: 輸出1。 延遲函數(shù)fmt.Println(aInt)的參數(shù)在defer語(yǔ)句出現(xiàn)時(shí)就已經(jīng)確定了, 后面修改的aInt變量實(shí)際上是拷貝了一份。所以無(wú)論后面如何修改aInt變量都不會(huì)影響延遲函數(shù)的執(zhí)行。
注意: 對(duì)于指針類型參數(shù), 規(guī)則仍然適用, 只不過(guò)延遲函數(shù)的參數(shù)是一個(gè)地址值, 這種情況下,defer后面的語(yǔ)句對(duì)變量的修改可能會(huì)影響延遲函數(shù)。
package main
import "fmt"
func main() {
deferArray()
}
func printArray(array *[3]int) {
for i := range array {
fmt.Println(array[i])
}
}
func deferArray() {
var aArray = [3]int{1, 2, 3}
defer printArray(&aArray)
aArray[0] = 10
return
}
結(jié)果:
函數(shù)說(shuō)明: 函數(shù)deferFuncParameter()定義一個(gè)數(shù)組, 通過(guò)defer延遲函數(shù)printArray()的調(diào)用, 最后修改數(shù)組第一個(gè)元素。 printArray()函數(shù)接受數(shù)組的指針并把數(shù)組全部打印出來(lái)。
參考答案: 輸出10、 2、 3三個(gè)值。 延遲函數(shù)printArray()的參數(shù)在defer語(yǔ)句出現(xiàn)時(shí)就已經(jīng)確定了, 即數(shù)組的地址, 由于延遲函數(shù)執(zhí)行時(shí)機(jī)是在return語(yǔ)句之前, 所以對(duì)數(shù)組的最終修改值會(huì)被打印出來(lái)。
規(guī)則二:defer延遲函數(shù)執(zhí)行按后進(jìn)先出順序執(zhí)行, 即先出現(xiàn)的defer最后執(zhí)行
定義defer類似于入棧操作, 執(zhí)行defer類似于出棧操作。
設(shè)計(jì)defer的初衷是簡(jiǎn)化函數(shù)返回時(shí)資源清理的動(dòng)作, 資源往往有依賴順序, 比如先申請(qǐng)A資源, 再跟據(jù)A資源申請(qǐng)B資源, 跟據(jù)B資源申請(qǐng)C資源, 即申請(qǐng)順序是:A—>B—>C, 釋放時(shí)往往又要反向進(jìn)行。 這就是把deffer設(shè)計(jì)成FIFO的原因。每申請(qǐng)到一個(gè)用完需要釋放的資源時(shí), 立即定義一個(gè)defer來(lái)釋放資源是個(gè)很好的習(xí)慣。
規(guī)則三: 延遲函數(shù)可能操作主函數(shù)的具名返回值
定義defer的函數(shù), 即主函數(shù)可能有返回值, 返回值有沒(méi)有名字沒(méi)有關(guān)系, defer所作用的函數(shù), 即延遲函數(shù)可能會(huì)影響到返回值。
package main
import "fmt"
func main() {
fmt.Println(test())
}
func test() (res int) {
a := 1
defer func() {
res ++
}()
return a
}
結(jié)果:
函數(shù)說(shuō)明: 函數(shù)擁有一個(gè)具名返回值result, 函數(shù)內(nèi)部聲明一個(gè)變量a, defer指定一個(gè)延遲函數(shù), 最后返回變量a。延遲函數(shù)中遞增res。
參考答案: 函數(shù)輸出2。 函數(shù)的return語(yǔ)句并不是原子的, 實(shí)際執(zhí)行分為設(shè)置返回值—>ret, defer語(yǔ)句實(shí)際執(zhí)行在返回前, 即擁有defer的函數(shù)返回過(guò)程是: 設(shè)置返回值—>執(zhí)行defer—>res。 所以return語(yǔ)句先把res設(shè)置為a的值, 即1, defer語(yǔ)句中又把res遞增1, 所以最終返回2。
return 返回值解析:
該函數(shù)的return語(yǔ)句可以拆分成下面兩行:
result = i
return
而延遲函數(shù)的執(zhí)行正是在return之前, 即加入defer后的執(zhí)行過(guò)程如下:
result = i
result++
return
一個(gè)主函數(shù)擁有一個(gè)匿名的返回值, 返回時(shí)使用字面值, 比如返回”1”、 ”2”、 ”Hello”這樣的值, 這種情況下defer語(yǔ)句是無(wú)法操作返回值的。
另外返回值是匿名類型的值,這種情況下defer語(yǔ)句可以引用到返回值, 但不會(huì)改變返回值。
package main
import "fmt"
func main() {
fmt.Println(test())
}
func test() int {
a := 1
defer func() {
a ++
}()
return a
}
func printArray(array *[3]int) {
for i := range array {
fmt.Println(array[i])
}
}
結(jié)果:
上面的函數(shù), 返回一個(gè)局部變量, 同時(shí)defer函數(shù)也會(huì)操作這個(gè)局部變量。 對(duì)于匿名返回值來(lái)說(shuō), 可以假定仍然有一個(gè)變量存儲(chǔ)返回值, 假定返回值變量為”anony”, 上面的返回語(yǔ)句可以拆分成以下過(guò)程:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-857658.html
anony = a
a++
return
由于a是整型, 會(huì)將值拷貝給anony, 所以defer語(yǔ)句中修改i值, 對(duì)函數(shù)返回值不造成影響。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-857658.html
總結(jié)
- defer定義的延遲函數(shù)參數(shù)在defer語(yǔ)句出時(shí)就已經(jīng)確定下來(lái)了
- defer定義順序與實(shí)際執(zhí)行順序相反
- return不是原子操作,執(zhí)行過(guò)程是: 保存返回值(若有)—>執(zhí)行defer( 若有) —>執(zhí)行ret跳轉(zhuǎn)
- 申請(qǐng)資源后立即使用defer關(guān)閉資源是好習(xí)慣
到了這里,關(guān)于golang學(xué)習(xí)筆記(defer基礎(chǔ)知識(shí))的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!