swift中defer的實際應用小結
看看蘋果官方的介紹
用 defer 語句在即將離開當前代碼塊時執行一系列語句。該語句讓你能執行一些必要的清理工作,不管是以何種方式離開當前代碼塊的——無論是由於拋出錯誤而離開,或是由於諸如 return、break 的語句。例如,你可以用 defer 語句來確保文件描述符得以關閉,以及手動分配的內存得以釋放。
defer 語句將代碼的執行延遲到當前的作用域退出之前。該語句由 defer 關鍵字和要被延遲執行的語句組成。延遲執行的語句不能包含任何控制轉移語句,例如 break、return 語句,或是拋出一個錯誤。延遲執行的操作會按照它們聲明的順序從後往前執行——也就是說,第一條 defer 語句中的代碼最後才執行,第二條 defer 語句中的代碼倒數第二個執行,以此類推。最後一條語句會第一個執行。
總結一下蘋果官方的介紹
defer語句在代碼塊(方法、閉包等,可以理解為大括號包裝起來的代碼)作用域退出之前\color{red}{作用域退出之前}作用域退出之前執行,也就是代碼塊中其他應該執行的代碼都執行完瞭,才執行defer中的代碼
一個代碼塊允許多個defer,多個defer執行的順序從後到前\color{red}{從後到前}從後到前
一些測試及誤區糾正
測試案例1
func testDefer() { defer { print("方法中defer內容") } if true { defer { print("if 中defer內容") } print("if中最後的代碼") } print("方法中的代碼") if true { return } print("方法結束前最後一句代碼") } testDefer()
以上代碼打印結果:
if中最後的代碼
if 中defer內容
方法中的代碼
方法中defer內容
打印結果中,第一個if中的代碼及裡面的defer最先執行,方法中的defer最後執行,由此可以看出,代碼塊中其他能夠執行的代碼先執行,最後執行defer的內容;defer的作用范圍不能簡單的看成方法,而是代碼塊(可能有些同學會有這樣的誤區)
測試案例2
func testDefer() { print("開始") defer { print("defer 1 中的內容") } defer { print("defer 2 中的內容") } if true { return } defer { print("defer 3 中的內容") } print("方法結束前最後一句代碼") } testDefer()
打印結果
開始
defer 2 中的內容
defer 1 中的內容
我們可以看到最後一個defer沒有執行,所以defer定義的位置很重要,如果沒有執行defer定義的代碼,在代碼塊結束前不會執行defer中的內容
多個defer的執行順序從後到前
一些實際應用場景
場景1:一些資源用完後需釋放,這裡給的是官方的一個案例
func processFile(filename: String) throws { if exists(filename) { let file = open(filename) defer { close(file) } while let line = try file.readline() { // 處理文件。 } // close(file) 會在這裡被調用,即作用域的最後。 } }
開始用到資源的時候就使用defer去釋放,避免忘記釋放資源
場景2:加鎖解鎖,借鑒瞭kingfisher
let lock = NSLock() func testDefer() { lock.lock() defer { lock.unlock() } doSomething() } testDefer()
在加鎖後立刻用defer解鎖,避免忘記解鎖
場景3:處理一些代碼塊作用域結束前的重復操作,比如請求網絡數據的時候
通常的一種寫法
func loadCityList(_ finish: ((Error?, [String]?) -> ())?) { DispatchQueue.global().async { // 模擬網絡請求 let data: AnyObject? // 模擬服務器返回的數據 guard let dict = data as? [String: AnyObject] else { DispatchQueue.main.async { finish?(error, nil) } return } guard let code = dict["code"] as? Int, code == 200 else { DispatchQueue.main.async { finish?(error, nil) } return } guard let citys = dict["data"] as? [String]? else { DispatchQueue.main.async { finish?(error, nil) } return } DispatchQueue.main.async { finish?(nil, citys) } } }
當每次有錯誤處理時和結果正確時都需要去做回調,而且回調可能有一堆代碼,看起來代碼會比較冗餘,而且在一些錯誤處理時很容易造成忘記回調
defer怎麼去寫呢
func loadCityList(_ finish: ((Error?, [String]?) -> ())?) { DispatchQueue.global().async { // 模擬網絡請求 var error: Error? = nil var citys: [String]? = nil defer { DispatchQueue.main.async { finish?(error, citys) } } let data: AnyObject? // 模擬服務器返回的數據 guard let dict = data as? [String: AnyObject] else { error = ... return } guard let code = dict["code"] as? Int, code == 200 else { error = ... return } guard let tempCitys = dict["data"] as? [String]? else { error = ... return } citys = tempCitys } }
使用defer既解決瞭代碼冗餘,又解決瞭可能忘記回調的問題,還有當我們看到defer時,我們很清楚知道,無論網絡請求結果如果,都會回調
總結
本文主要介紹瞭defer的定義、作用及一些用法
到此這篇關於swift中defer的實際應用的文章就介紹到這瞭,更多相關swift中defer應用內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Go語言函數的延遲調用(Deferred Code)詳解
- Golang 的defer執行規則說明
- Golang之defer 延遲調用操作
- Go語言學習函數+結構體+方法+接口
- 聊聊golang中多個defer的執行順序