網站首頁 編程語言 正文
先了解什么是defer
Go語言中的defer與return執行的先后順序
Go語言的 defer 語句會將其后面跟隨的語句進行延遲處理,在 defer 歸屬的函數即將返回時,將延遲處理的語句按 defer 的逆序進行執行.也就是說,先被 defer 的語句最后被執行,最后被 defer 的語句,最先被執行。(與棧的先入后出是一個道理,也可以將其理解為入棧和出棧)
舉一個簡單的例子
func main() { a, b := 111, 333 defer fmt.Println("b= ", b) fmt.Println("a= ", a) } 打印結果: a= 111 b= 333
可以看到雖然執行語句時b在前,但是輸出結果為b在最后被輸出。
defer 的用法
(簡單講解,細節請自行查閱資料)
一般用來釋放資源或者讀寫操作,當處理業務或邏輯中涉及成對的操作是一件比較煩瑣的事情,比如打開和關閉文件、接收請求和回復請求、加鎖和解鎖等。在這些操作中,最容易忽略的就是在每個函數退出處正確地釋放和關閉資源。比如下面一個例子
func main(){ a := 1 out := bufio.NewWriter(os.Stdout) defer out.Flush() fmt.Fprintln(out, a) } 輸出結果: 1
就可以在最后將結果打印到控制臺中去,類似的用法如關閉數據庫資源等等。如果這個例子太過于簡單,那么來看下個例子。
var a bool = true defer func() { fmt.Println("1") }() if a == true { fmt.Println("2") return } defer func() { fmt.Println("3") }() 輸出結果: 2 1
我們會發現defer語句也是需要被執行的,如果在defer函數執行之前就執行return。defer后的語句就不會再被執行了。但是如果是在return之前defer已經執行,則defer中的語句將會在return執行之前先一步進行執行.
那么defer 和 return有什么聯系?
defer 是延遲執行語句,return是返回語句,那么肯定出現誰先誰后的問題。下面看一個經典的例子吧
func increaseA() int { var i int defer func() { i++ }() return i } func increaseB() (r int) { defer func() { r++ }() return r } func main() { fmt.Println(increaseA()) fmt.Println(increaseB()) } 輸出結果為: 0 1
肯定有人覺得有疑惑,為什么A函數沒有輸出,B函數卻輸出了呢?為什么答案不是1和0呢?
原因:
先說結論:defer 修飾的匿名函數,只能更新具名返回值.那么這會導致什么問題呢?我們來逐步分析這個例子。
- 在increaseA()函數中有一個聲明i,表示i在該函數內已經被生成,是有名稱的變量。但該函數返回參數為匿名參數.
- 在increaseB()函數中沒有聲明r,是匿名變量。但該函數返回參數為具名參數.
- func increaseA() int,返回值i=0的時候該值已經被綁定到返回值里了,defer再去改i已經沒用了.
- func increaseB() (r int), 返回值r先把返回變量設為0,defer又把r改為1.這時候還能生效. 因此答案很明顯為 1 和 0.
更進一步理解
我們若想要進一步理解也可以去輸出匯編語句,然后進行研讀,可惜我是個菜鳥讀不懂匯編語言!但我們可以從return入手
我們要理解return 返回值的運行機制:
return
并非原子操作,分為賦值,和返回值兩步操作.實際上return
執行了兩步操作,因為返回值沒有命名,所以return
默認指定了一個返回值(假設為a),首先將i賦值給a,后續的操作因為是針對i進行的,所以不會影響a, 此后因為a不會更新,所以return a
不會改變.
var i int a := i return a
但是如果return的參數a是具名參數,就像上述例子中increaseB()函數一樣。a就相當于命名的變量i, 因為所有的操作都是基于命名變量i(a),返回值也是i, 所以每一次defer操作,都會更新返回值i.
省流小結
return會將返回值先保存起來,對于無名返回值來說,保存在一個臨時對象中,defer是看不到這個臨時對象的;而對于有名返回值來說,就保存在已命名的變量中。
原文鏈接:https://juejin.cn/post/7171066100052918308
相關推薦
- 2022-08-30 Linux常用tar命令
- 2022-05-15 go?RWMutex的實現示例_Golang
- 2022-07-25 Android開發之Fragment懶加載的幾種方式及性能對比_Android
- 2022-06-18 Android自定義彈框Dialog效果_Android
- 2022-06-25 Python+PuLP實現線性規劃的求解_python
- 2022-07-21 css讓不定寬高的div,垂直水平居中
- 2022-07-27 關于pytest結合csv模塊實現csv格式的數據驅動問題_python
- 2022-01-30 使用ref手動改變antd的搜索框Input.Search的搜索內容
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支