guard 是一個要求表達式的值為 true 從而繼續(xù)執(zhí)行的條件語句桑阶。如果表達式為 false香到,則會執(zhí)行必須提供的 else 分支挑童。
func sayHello(numberOfTimes: Int) -> () {
guard numberOfTimes > 0 else {
return
}
for _ in 1...numberOfTimes {
print("Hello!")
}
}
guard
語句中的 else
分支必須退出當前的區(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)在書的結尾呀打,謝謝你U俚鳌)。
請注意 throw 語句離檢查本身有多遠呕寝。你需要讀完整個方法來找到如果沒有 book.txt 會發(fā)生什么空执。
像一本好書一樣浪箭,代碼應該講述一個故事:有著易懂的情節(jié),清晰的開端辨绊、發(fā)展和結尾奶栖。(請嘗試不要寫太多「后現(xiàn)代」風格的代碼。)
使用 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)")
}
這樣就好多了邢羔! 每一個錯誤都在相應的檢查之后立刻被拋出驼抹,所以我們可以按照左手邊的代碼順序來梳理工作流的順序。
defer
defer
關鍵字為此提供了安全又簡單的處理方式:聲明一個 block拜鹤,當前代碼執(zhí)行的閉包退出時會執(zhí)行該 block框冀。
看看下面這個包裝了系統(tǒng)調用 gethostname(2)
的函數(shù),用來返回當前系統(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ā)生后銷毀它明也,又要在正常流程下不再使用它時對其進行銷毀宣虾。
這種設計很容易導致錯誤,而且不停地在做重復工作温数。
通過使用 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:) 調用之后,但它要等到當前區(qū)域結束時才會被執(zhí)行撑刺。多虧了 defer鹉胖,buffer 才能無論在哪個點退出函數(shù)都可以被釋放。
考慮在任何需要配對調用的 API 上都使用 defer
够傍,比如 allocate(capacity:) / deallocate()甫菠、wait() / signal() 和 open() / close()。
如果在 defer 語句中引用了一個變量冕屯,執(zhí)行時會用到變量最終的值寂诱。換句話說:defer 代碼塊不會捕獲變量當前的值。
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/