guard & defer

guard 是一個要求表達式的值為 true 從而繼續(xù)執(zhí)行的條件語句。如果表達式為 false,則會執(zhí)行必須提供的 else 分支。

 func sayHello(numberOfTimes: Int) -> () {
        guard numberOfTimes > 0 else {
            return
        }
        
        for _ in 1...numberOfTimes {
            print("Hello!")
        }
        
    }

guard 語句中的 else 分支必須退出當(dāng)前的區(qū)域,通過使用 return 來退出函數(shù),continue 或者 break來退出循環(huán),或者使用像 fatalError(_:file:line:) 這種返回 Never 的函數(shù)。

使用 guard 來避免過多的縮進和錯誤

比如,我們要實現(xiàn)一個 readBedtimeStory() 函數(shù):

enum StoryError:Error {
        case missing
        case illegible
        case tooScary
    }
    
    //未使用guard的函數(shù)
    func readBedtimeStory() throws {
        if let url = Bundle.main.url(forResource: "books", withExtension: "txt") {
            
            if let data = try? Data(contentsOf: url), let story = String(data: data, encoding: .utf8) {
                if story.contains("??") {
                    throw StoryError.tooScary
                } else {
                    print("Once upon a time...\(story)")
                }
            } else {
                throw StoryError.illegible
            }
            
        } else {
            throw StoryError.missing
        }
    }

要讀一個睡前故事,我們需要能找到一本書,這本故事書必須要是可讀的,并且故事不能太嚇人(請不要讓怪物出現(xiàn)在書的結(jié)尾,謝謝你?。?。

請注意 throw 語句離檢查本身有多遠。你需要讀完整個方法來找到如果沒有 book.txt 會發(fā)生什么。

像一本好書一樣,代碼應(yīng)該講述一個故事:有著易懂的情節(jié),清晰的開端、發(fā)展和結(jié)尾。(請嘗試不要寫太多「后現(xiàn)代」風(fēng)格的代碼。)

使用 guard 語句組織代碼可以讓代碼讀起來更加的線性:

//使用guard
    func readBedTimeStory2() throws {
        
        guard let url = Bundle.main.url(forResource: "books", withExtension: "txt") else {
            throw StoryError.missing
        }
        
        guard let data = try? Data(contentsOf: url), let story = String(data: data, encoding: .utf8) else {
            throw StoryError.illegible
        }
        
        if story.contains("??") {
            throw StoryError.tooScary
        }
        
        print("Once upon a time...\(story)")
        
    }

這樣就好多了! 每一個錯誤都在相應(yīng)的檢查之后立刻被拋出,所以我們可以按照左手邊的代碼順序來梳理工作流的順序。

defer

defer 關(guān)鍵字為此提供了安全又簡單的處理方式:聲明一個 block,當(dāng)前代碼執(zhí)行的閉包退出時會執(zhí)行該 block。

看看下面這個包裝了系統(tǒng)調(diào)用 gethostname(2) 的函數(shù),用來返回當(dāng)前系統(tǒng)的主機名稱

import Darwin

func currenthostName() -> String {
        let capacity = Int(NI_MAXHOST)
        let buffer = UnsafeMutablePointer<Int8>.allocate(capacity: capacity)
        
        guard gethostname(buffer, capacity) == 0 else {
            buffer.deallocate()
            return "localhost"
        }
        
        let hostname = String(cString: buffer)
        buffer.deallocate()
        
        return hostname
        
        
    }

這里有一個在最開始就創(chuàng)建的 UnsafeMutablePointer<UInt8> 用于存儲目標數(shù)據(jù),但是我既要在錯誤發(fā)生后銷毀它,又要在正常流程下不再使用它時對其進行銷毀。

這種設(shè)計很容易導(dǎo)致錯誤,而且不停地在做重復(fù)工作。

通過使用 defer 語句,我們可以排除潛在的錯誤并且簡化代碼:

func currentHostName2() -> String {
        let capacity = Int(NI_MAXHOST)
        let buffer = UnsafeMutablePointer<Int8>.allocate(capacity: capacity)
        defer {
            buffer.deallocate()
        }
        
        guard gethostname(buffer, capacity) == 0 else {
            return "localhost"
        }
        
        return String(cString: buffer)
        
    }

盡管 defer 緊接著出現(xiàn)在 allocate(capacity:) 調(diào)用之后,但它要等到當(dāng)前區(qū)域結(jié)束時才會被執(zhí)行。多虧了 defer,buffer 才能無論在哪個點退出函數(shù)都可以被釋放。

考慮在任何需要配對調(diào)用的 API 上都使用 defer,比如 allocate(capacity:) / deallocate()、wait() / signal() 和 open() / close()。
如果在 defer 語句中引用了一個變量,執(zhí)行時會用到變量最終的值。換句話說:defer 代碼塊不會捕獲變量當(dāng)前的值。

func flipFlop() -> () {
        var position = "It's pronounced /haha/"
        defer {
            print(position)
        }
        
        position = "It's pronounced /hehe/"
        defer {
            print(position)
        }
        
    }

輸出:
It's pronounced /hehe/
It's pronounced /hehe/
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • ORA-00001: 違反唯一約束條件 (.) 錯誤說明:當(dāng)在唯一索引所對應(yīng)的列上鍵入重復(fù)值時,會觸發(fā)此異常。 O...
    我想起個好名字閱讀 5,966評論 0 9
  • 第2章 基本語法 2.1 概述 基本句法和變量 語句 JavaScript程序的執(zhí)行單位為行(line),也就是一...
    悟名先生閱讀 4,556評論 0 13
  • 1. vigilant: giving careful attention to what is happenin...
    TRICCCC閱讀 246評論 0 0
  • 夜登山,自無言。 妄談神機獨成圓。 長道非古跡, 點點昏黃盞。 偶見魑魅影, 難分狻猊形。 來往談笑不孑立, 聲聲...
    點櫻閱讀 428評論 1 5
  • 今天又重溫了星爺?shù)碾娪啊断矂≈酢?,不如從幾個主人翁的角度來談一下觀后感。 “努力奮斗”
    Devil_3b37閱讀 198評論 0 1

友情鏈接更多精彩內(nèi)容