swift之defer 幾個(gè)簡單的使用場景

defer 是干什么的
一句話概括就是 defer block 里的代碼會(huì)在函數(shù) return 之前執(zhí)行惯雳,無論函數(shù)是從哪個(gè)分支 return 的,還是有 throw的石景,還是自然而然走到最后一行的拙吉。

這個(gè)關(guān)鍵字就跟 Java 里的 try-catch-finally 的finally一樣,不管 try catch 走哪個(gè)分支揪荣,它都會(huì)在函數(shù) return 之前執(zhí)行。而且它比 Java 的finally還更強(qiáng)大的一點(diǎn)是仗颈,它可以獨(dú)立于 try catch 存在,

所以它可以成為整理函數(shù)流程的幫手挨决。
在函數(shù) return 之前無論如何都要做的處理,
可以放進(jìn)這個(gè) block 里脖祈,讓代碼看起來更干凈舒服损拢。

swift 文檔上的例子:

var fridgeIsOpen = false
let fridgeContent = ["milk", "eggs", "leftovers"]
 
func fridgeContains(_ food: String) -> Bool {
    fridgeIsOpen = true
    defer {
        fridgeIsOpen = false
    }
    
    let result = fridgeContent.contains(food)
    return result
}
fridgeContains("banana")
print(fridgeIsOpen)

例子里執(zhí)行的順序是:
先fridgeIsOpen = true福压,
然后是函數(shù)體正常的流程,
最后在 return 之前執(zhí)行 fridgeIsOpen = false或舞。

簡單的幾個(gè)使用場景

try catch 結(jié)構(gòu)

最典型的場景,我想也是 defer 這個(gè)關(guān)鍵字誕生的主要原因吧:

func foo() {
  defer {
    print("finally")
  }
  do {
    throw NSError()
    print("impossible")
  } catch {
    print("handle error")
  }
}

不管 do block 是否 throw error映凳,有沒有 catch 到,還是 throw 出去了诈豌,都會(huì)保證在整個(gè)函數(shù) return 前執(zhí)行 defer。
在這個(gè)例子里就是:
先 print 出 "handle error"
再 print 出 "finally"矫渔。

do block 里也可以寫 defer:
do {
  defer {
    print("finally")
  }
  throw NSError()
  print("impossible")
} catch {
  print("handle error")
}

那么它執(zhí)行的順序就會(huì)是在 catch block 之前彤蔽,也就是先 print 出 "finally" 再 print 出 "handle error"庙洼。

清理工作、回收資源

跟 swift 文檔舉的例子類似油够,defer一個(gè)很適合的使用場景就是用來做清理工作。比如文件操作:

關(guān)閉文件

func foo() {
  let fileDescriptor = open(url.path, O_EVTONLY)
  defer {
    close(fileDescriptor)
  }
  // use fileDescriptor...
}

可以避免 哪個(gè)分支忘了寫石咬,或者中間 throw 個(gè) error,導(dǎo)致 fileDescriptor 沒法正常關(guān)閉鬼悠。

dealloc 手動(dòng)分配的空間
func foo() {
  let valuePointer = UnsafeMutablePointer.allocate(capacity: 1)
  defer {
    valuePointer.deallocate(capacity: 1)
  }
  // use pointer...
}

加/解鎖:下面是 swift 里類似 Objective-C 的 synchronized block 的一種寫法亏娜,可以使用任何一個(gè) NSObject 作 lock
成對調(diào)用 可以用 defer 把它們放在一起。

func foo() {
  objc_sync_enter(lock)
  defer { 
    objc_sync_exit(lock)
  }
  // do something...
}
調(diào) completion block

有時(shí)候一個(gè)函數(shù)分支比較多可能某個(gè)小分支 return 之前就忘了調(diào) completion block镇匀,結(jié)果藏下 bug。

func foo(completion: () -> Void) {
  defer {
    self.isLoading = false
    completion()
  }
  guard error == nil else { return } 
  // handle success
}

有時(shí)候 completion 要根據(jù)情況傳不同的參數(shù)汗侵,這時(shí) defer 就不好用了。如果 completion block 被存下來了晰韵,我們還是可以用它來確保執(zhí)行后能釋放:

func foo() {
  defer {
    self.completion = nil
  }
  if (succeed) {
    self.completion(.success(result))
  } else {
    self.completion(.error(error))
  }
}
調(diào) super 方法

有時(shí)候 override 一個(gè)方法,主要目的是在 super 方法之前做一些準(zhǔn)備工作雪猪,比如 UICollectionViewLayout 的 prepare(forCollectionViewUpdates:)栏尚,那么我們就可以把調(diào)用 super 的部分放在 defer 里:

func override foo() {
  defer {
    super.foo()
  }
  // some preparation before super.foo()...
}
一些細(xì)節(jié)

任意 scope 都可以有 defer

雖然大部分的使用場景是在函數(shù)里只恨,不過理論上任何一個(gè) { } 之間都是可以寫 defer 的。比如一個(gè)普通的循環(huán):

var sumOfOdd = 0
for i in 0...10 {
  defer {
    print("Look! It's \(i)")
  }
  if i % 2 == 0 {
    continue
  }
  sumOfOdd += i
}

continue 或者 break 都不會(huì)妨礙 defer 的執(zhí)行官觅。甚至 closure 里也可以寫 defer:

{
  defer { print("bye!") }
  print("hello!")
}

只不過 這樣沒什么意義……

必須執(zhí)行到 defer 才會(huì)觸發(fā)

假設(shè)有這樣一個(gè)問題:一個(gè) scope 里的 defer 能保證一定會(huì)執(zhí)行嗎?

答案 NO……比如下面這個(gè)例子:

func foo() throws {
  do {
    throw NSError()
    print("impossible")
  }
  defer {
    print("finally")
  }
}
try?foo()

不會(huì)執(zhí)行 defer休涤,不會(huì) print 任何東西。這個(gè)故事告訴我們功氨,至少要執(zhí)行到 defer 這一行序苏,它才保證后面會(huì)觸發(fā)捷凄。同樣道理提前 return 也是一樣不行的:

func foo() {
  guard false else { return }
  defer {
    print("finally")
  }
}
多個(gè) defer

一個(gè) scope 可以有多個(gè) defer,順序是像棧一樣FIFO執(zhí)行的:每遇到一個(gè) defer 就像壓進(jìn)一個(gè)棧里纵势,到 scope 結(jié)束的時(shí)候踱阿,后進(jìn)棧的先執(zhí)行钦铁。如下面的代碼才漆,會(huì)按 1、2醇滥、3超营、4、5阅虫、6 的順序 print 出來。

func foo() {
  print("1")
  defer {
    print("6")
  }
  print("2")
  defer {
    print("5")
  }
  print("3")
  defer {
    print("4")
  }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末颓帝,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子购城,更是在濱河造成了極大的恐慌,老刑警劉巖瘪板,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異侮攀,居然都是意外死亡锣枝,警方通過查閱死者的電腦和手機(jī)兰英,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來箭昵,“玉大人,你說我怎么就攤上這事家制≌郑” “怎么了颤殴?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長涵但。 經(jīng)常有香客問我,道長矮瘟,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任澈侠,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘烧栋。我一直安慰自己写妥,他們只是感情好审姓,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著魔吐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪画畅。 梳的紋絲不亂的頭發(fā)上砸琅,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天症脂,我揣著相機(jī)與錄音,去河邊找鬼淫僻。 笑死,一個(gè)胖子當(dāng)著我的面吹牛雳灵,可吹牛的內(nèi)容都是我干的棕所。 我是一名探鬼主播悯辙,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼躲撰!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起拢蛋,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谆棱,沒想到半個(gè)月后快压,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體垃瞧,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年个从,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片信姓。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖意推,靈堂內(nèi)的尸體忽然破棺而出豆瘫,到底是詐尸還是另有隱情菊值,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布腻窒,位于F島的核電站,受9級(jí)特大地震影響儿子,放射性物質(zhì)發(fā)生泄漏瓦哎。R本人自食惡果不足惜柔逼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望愉适。 院中可真熱鬧,春花似錦维咸、人聲如沸剂买。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽费坊。三九已至倒槐,卻和暖如春附井,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背永毅。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國打工把跨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留沼死,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像耸别,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子秀姐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容