Swift 中 defer 的介紹與使用場(chǎng)景
用defer語(yǔ)句在即將離開(kāi)當(dāng)前代碼塊時(shí)執(zhí)行一系列語(yǔ)句。該語(yǔ)句讓你能執(zhí)行一些必要的善后工作(比如偶惠,關(guān)閉,清理,回調(diào))不管是以何種方式離開(kāi)當(dāng)前代碼塊的---無(wú)論是由于拋出錯(cuò)誤而離開(kāi)献宫,或者是由于諸如return,break的語(yǔ)句实撒。
例如:你可以用defer語(yǔ)句來(lái)確保文件描述符得以關(guān)閉姊途,以及手動(dòng)分配的內(nèi)存得以釋放。
defer語(yǔ)句將代碼的執(zhí)行延遲到當(dāng)前的作用域退出之前知态。該語(yǔ)句由defer關(guān)鍵字和要被延遲的語(yǔ)句組成捷兰。延遲執(zhí)行的語(yǔ)句不能包括任何控制轉(zhuǎn)移語(yǔ)句,例如break负敏,return語(yǔ)句贡茅,或是拋出一個(gè)錯(cuò)誤,延遲執(zhí)行的操作會(huì)按照它們聲明的順序從后往前執(zhí)行---也就是說(shuō)其做,第一條defer語(yǔ)句中的代碼最后才執(zhí)行顶考,第二條defer語(yǔ)句中的代碼倒數(shù)第二個(gè)執(zhí)行,以此類推庶柿,最后一條語(yǔ)句會(huì)第一個(gè)執(zhí)行村怪。
defer 語(yǔ)句在代碼塊(方法,閉包等浮庐,可以理解為大括號(hào)包裝起來(lái)的代碼)作用域退出之前執(zhí)行甚负,也就是代碼塊中其他代碼該執(zhí)行的都執(zhí)行完了,才執(zhí)行defer中的代碼一個(gè)代碼塊允許多個(gè)defer审残,多個(gè)defer執(zhí)行的順序從后到前梭域。
測(cè)試案例1
func testDefer() {
defer {
print("方法中defer內(nèi)容") // 4
}
if true {
defer {
print("if 中defer內(nèi)容") // 2
}
print("if中最后的代碼") // 1
}
print("方法中的代碼") // 3
if true {
return
}
print("方法結(jié)束前最后一句代碼") // 前面return 掉了 這里不執(zhí)行
}
testDefer()
執(zhí)行結(jié)果
if中最后的代碼
if 中defer內(nèi)容
方法中的代碼
方法中defer內(nèi)容
打印結(jié)果中,第一個(gè) if true 中的代碼塊先執(zhí)行(if中的 最后的代碼先執(zhí)行搅轿,然后是 if中的 defer 代碼塊)病涨,然后是 if下方的 方法中的代碼執(zhí)行,最后執(zhí)行testDefer方法中的 defer 代碼塊璧坟。
由此可以看出既穆,代碼塊中其他能夠執(zhí)行的代碼先執(zhí)行,最后執(zhí)行defer的內(nèi)容雀鹃;defer的作用范圍不能簡(jiǎn)單的看成方法幻工,而是代碼塊
測(cè)試案例2
func testDefer() {
print("開(kāi)始") // 1
defer {
print("defer 1 中的內(nèi)容") // 4
}
defer {
print("defer 2 中的內(nèi)容") // 3
}
print("方法中的代碼") // 2
if true {
return
}
defer {
print("defer 3 中的內(nèi)容")
}
print("方法結(jié)束前最后一句代碼")
}
testDefer()
結(jié)果
開(kāi)始
方法中的代碼
defer 2 中的內(nèi)容
defer 1 中的內(nèi)容
我們可以看出 if true 之后的代碼 都沒(méi)有執(zhí)行,包括最后一個(gè) defer 3 也沒(méi)有執(zhí)行黎茎,所以defer定義的位置很重要囊颅,如果沒(méi)有執(zhí)行defer定義的代碼,在代碼塊結(jié)束前,不會(huì)執(zhí)行defer中的內(nèi)容踢代。
注意:多個(gè)defer的執(zhí)行順序從后到前
一些實(shí)際應(yīng)用場(chǎng)景
場(chǎng)景1 - 一些資源用完后需要釋放盲憎,這里給的是官方的一個(gè)案例
func processFile(filename: String) throws {
if exists(filename) {
let file = open(filename)
defer {
close(file)
}
while let line = try file.readline() {
// 處理文件。
}
// close(file) 會(huì)在這里被調(diào)用胳挎,即作用域的最后饼疙。
}
}
開(kāi)始用到資源的時(shí)候就是用defer去釋放,避免忘記釋放資源串远。
場(chǎng)景2 - 加鎖解鎖宏多,借鑒了 kingfisher
let lock = NSLock()
func testDefer() {
lock.lock()
defer {
lock.unlock()
}
print("doSomething。澡罚。伸但。")
}
testDefer()
在加鎖后,添加defer代碼解鎖留搔,避免忘記解鎖更胖。
場(chǎng)景3 - 處理一些代碼塊作用域結(jié)束前的重復(fù)操作,如調(diào) completion block
這是一個(gè)讓我感覺(jué)“如果當(dāng)時(shí)知道 defer
”就好了的場(chǎng)景隔显,就是有時(shí)候一個(gè)函數(shù)分支比較多却妨,可能某個(gè)小分支 return 之前就忘了調(diào) completion block,結(jié)果藏下一個(gè)不易發(fā)現(xiàn)的 bug括眠。用 defer
就可以不用擔(dān)心這個(gè)問(wèn)題了
func foo(completion: () -> Void) {
defer {
self.isLoading = false
completion()
}
guard error == nil else { return }
// handle success
}
有時(shí)候 completion 要根據(jù)情況傳不同的參數(shù)彪标,這時(shí) defer
就不好使了。不過(guò)如果 completion block 被存下來(lái)了掷豺,我們還是可以用它來(lái)確保執(zhí)行后能釋放:
func foo() {
defer {
self.completion = nil
}
if (succeed) {
self.completion(.success(result))
} else {
self.completion(.error(error))
}
}
場(chǎng)景4 調(diào)用supper方法
有時(shí)候 override 一個(gè)方法捞烟, 主要目的是在super方法之前做一些準(zhǔn)備工作,我們就可以把調(diào)用 super 的部分放在 defer
里面:
func override foo() {
defer {
super.foo()
}
// some preparation before super.foo()...
}
建議:最好不要加多個(gè) defer 否則邏輯更加會(huì)顯著比較混亂当船。
大家多喝熱水题画,最近天真干。