最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Swift中defer的正確使用方法

 更新時(shí)間:2018年11月18日 11:44:08   作者:OneV''''s Den  
準(zhǔn)備把 swift 文檔再掃一遍,發(fā)現(xiàn)了defer這個(gè)關(guān)鍵字,所以下面這篇文章主要給大家介紹了關(guān)于Swift中defer的正確使用方法,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下

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ù)流程的一個(gè)小幫手。在函數(shù) return 之前無論如何都要做的處理,可以放進(jìn)這個(gè) block 里,讓代碼看起來更干凈一些~

其實(shí)這篇文章的緣起是由于在對(duì) Kingfisher 做重構(gòu)的時(shí)候,因?yàn)樽约簩?duì) defer 的理解不夠準(zhǔn)確,導(dǎo)致了一個(gè) bug。所以想藉由這篇文章探索一下 defer 這個(gè)關(guān)鍵字的一些 edge case。

典型用法

Swift 里的 defer 大家應(yīng)該都很熟悉了,defer 所聲明的 block 會(huì)在當(dāng)前代碼執(zhí)行退出后被調(diào)用。正因?yàn)樗峁┝艘环N延時(shí)調(diào)用的方式,所以一般會(huì)被用來做資源釋放或者銷毀,這在某個(gè)函數(shù)有多個(gè)返回出口的時(shí)候特別有用。比如下面的通過 FileHandle 打開文件進(jìn)行操作的方法:

func operateOnFile(descriptor: Int32) {
let fileHandle = FileHandle(fileDescriptor: descriptor)

let data = fileHandle.readDataToEndOfFile()

if /* onlyRead */ {
fileHandle.closeFile()
return
}

let shouldWrite = /* 是否需要寫文件 */
guard shouldWrite else {
fileHandle.closeFile()
return
}

fileHandle.seekToEndOfFile()
fileHandle.write(someData)
fileHandle.closeFile()
}

我們在不同的地方都需要調(diào)用 fileHandle.closeFile() 來關(guān)閉文件,這里更好的做法是用 defer 來統(tǒng)一處理。這不僅可以讓我們就近在資源申請的地方就聲明釋放,也減少了未來添加代碼時(shí)忘記釋放資源的可能性:

func operateOnFile(descriptor: Int32) {
let fileHandle = FileHandle(fileDescriptor: descriptor)
defer { fileHandle.closeFile() }
let data = fileHandle.readDataToEndOfFile()

if /* onlyRead */ { return }

let shouldWrite = /* 是否需要寫文件 */
guard shouldWrite else { return }

fileHandle.seekToEndOfFile()
fileHandle.write(someData)
}

defer 的作用域

在做 Kingfisher 重構(gòu)時(shí),對(duì)線程安全的保證我選擇使用了 NSLock 來完成。簡單說,會(huì)有一些類似這樣的方法:

let lock = NSLock()
let tasks: [ID: Task] = [:]

func remove(_ id: ID) {
lock.lock()
defer { lock.unlock() }
tasks[id] = nil
}

對(duì)于 tasks 的操作可能發(fā)生在不同線程中,用 lock() 來獲取鎖,并保證當(dāng)前線程獨(dú)占,然后在操作完成后使用 unlock() 釋放資源。這是很典型的 defer 的使用方式。

但是后來出現(xiàn)了一種情況,即調(diào)用 remove 方法之前,我們在同一線程的 caller 中獲取過這個(gè)鎖了,比如:

func doSomethingThenRemove() {
lock.lock()
defer { lock.unlock() }

// 操作 `tasks`
// ...

// 最后,移除 `task`
remove(123)
}

這樣做顯然在 remove 中造成了死鎖 (deadlock):remove 里的 lock() 在等待 doSomethingThenRemove 中做 unlock() 操作,而這個(gè) unlock 被 remove 阻塞了,永遠(yuǎn)不可能達(dá)到。

解決的方法大概有三種:

  1. 換用 NSRecursiveLock:NSRecursiveLock 可以在同一個(gè)線程獲取多次,而不造成死鎖的問題。
  2. 在調(diào)用 remove 之前先 unlock。
  3. 為 remove 傳入按照條件,避免在其中加鎖。

1 和 2 都會(huì)造成額外的性能損失,雖然在一般情況下這樣的加鎖性能微乎其微,但是使用方案 3 似乎也并不很麻煩。于是我很開心地把 remove 改成了這樣:

func remove(_ id: ID, acquireLock: Bool) {
if acquireLock {
lock.lock()
defer { lock.unlock() }
}
tasks[id] = nil
}

很好,現(xiàn)在調(diào)用 remove(123, acquireLock: false) 不再會(huì)死鎖了。但是很快我發(fā)現(xiàn),在 acquireLock 為 true 的時(shí)候鎖也失效了。再仔細(xì)閱讀 Swift Programming Language 關(guān)于 defer 的描述:

A defer statement is used for executing code just before transferring program control outside of the scope that the defer statement appears in.

所以,上面的代碼其實(shí)相當(dāng)于:

func remove(_ id: ID, acquireLock: Bool) {
if acquireLock {
lock.lock()
lock.unlock()
}
tasks[id] = nil
}

GG 斯密達(dá)…

以前很單純地認(rèn)為 defer 是在函數(shù)退出的時(shí)候調(diào)用,并沒有注意其實(shí)是當(dāng)前 scope 退出的時(shí)候調(diào)用這個(gè)事實(shí),造成了這個(gè)錯(cuò)誤。在 if,guard,for,try 這些語句中使用 defer 時(shí),應(yīng)該要特別注意這一點(diǎn)。

defer 和閉包

另一個(gè)比較有意思的事實(shí)是,雖然 defer 后面跟了一個(gè)閉包,但是它更多地像是一個(gè)語法糖,和我們所熟知的閉包特性不一樣,并不會(huì)持有里面的值。比如:

func foo() {
var number = 1
defer { print("Statement 2: \(number)") }
number = 100
print("Statement 1: \(number)")
}

將會(huì)輸出:

Statement 1: 100
Statement 2: 100

在 defer 中如果要依賴某個(gè)變量值時(shí),需要自行進(jìn)行復(fù)制:

func foo() {
var number = 1
var closureNumber = number
defer { print("Statement 2: \(closureNumber)") }
number = 100
print("Statement 1: \(number)")
}

// Statement 1: 100
// Statement 2: 1

defer 的執(zhí)行時(shí)機(jī)

defer 的執(zhí)行時(shí)機(jī)緊接在離開作用域之后,但是是在其他語句之前。這個(gè)特性為 defer 帶來了一些很“微妙”的使用方式。比如從 0 開始的自增:

class Foo {
var num = 0
func foo() -> Int {
defer { num += 1 }
return num
}

// 沒有 `defer` 的話我們可能要這么寫
// func foo() -> Int {
// num += 1
// return num - 1
// }
}

let f = Foo()
f.foo() // 0
f.foo() // 1
f.num // 2

輸出結(jié)果 foo() 返回了 +1 之前的 num,而 f.num 則是 defer 中經(jīng)過 +1 之后的結(jié)果。不使用 defer 的話,我們其實(shí)很難達(dá)到這種“在返回后進(jìn)行操作”的效果。

雖然很特殊,但是強(qiáng)烈不建議在 defer 中執(zhí)行這類 side effect。

This means that a defer statement can be used, for example, to perform manual resource management such as closing file descriptors, and to perform actions that need to happen even if an error is thrown.

從語言設(shè)計(jì)上來說,defer 的目的就是進(jìn)行資源清理和避免重復(fù)的返回前需要執(zhí)行的代碼,而不是用來以取巧地實(shí)現(xiàn)某些功能。這樣做只會(huì)讓代碼可讀性降低。

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • Swift hello world!Swift快速入門教程

    Swift hello world!Swift快速入門教程

    這篇文章主要介紹了Swift hello world!Swift快速入門教程,本文在快速了解Swift編程語言,需要的朋友可以參考下
    2014-07-07
  • Swift?中的?RegexBuilder學(xué)習(xí)指南

    Swift?中的?RegexBuilder學(xué)習(xí)指南

    這篇文章主要為大家介紹了Swift中的RegexBuilder學(xué)習(xí)指南,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • Swift如何在應(yīng)用中添加圖標(biāo)更換功能的方法

    Swift如何在應(yīng)用中添加圖標(biāo)更換功能的方法

    本篇文章主要介紹了Swift如何在應(yīng)用中添加圖標(biāo)更換功能的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-02-02
  • mac git xcrun error active developer path 錯(cuò)誤

    mac git xcrun error active developer path 錯(cuò)誤

    本文主要是講訴了如何解決在mac下使用git;xcode4.6的環(huán)境時(shí),出現(xiàn)了錯(cuò)誤(mac git xcrun error active developer path)的解決辦法,希望對(duì)大家有所幫助
    2014-09-09
  • iOS開發(fā)中Swift 指紋驗(yàn)證功能模塊實(shí)例代碼

    iOS開發(fā)中Swift 指紋驗(yàn)證功能模塊實(shí)例代碼

    本文給大家分享ios調(diào)用touchid代碼塊,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下把
    2017-03-03
  • Swift圖像處理之優(yōu)化照片

    Swift圖像處理之優(yōu)化照片

    Core Image能通過分析圖片的各個(gè)屬性,人臉的區(qū)域等進(jìn)行自動(dòng)優(yōu)化圖片。我們只需要調(diào)用autoAdjustmentFiltersWithOptions這個(gè)API方法獲取各個(gè)自動(dòng)增強(qiáng)濾鏡來優(yōu)化圖片即可。不管是人物照片還是風(fēng)景照均可增強(qiáng)效果
    2015-11-11
  • Swift讀取App的版本信息與PCH文件詳解

    Swift讀取App的版本信息與PCH文件詳解

    這篇文章主要介紹了Swift讀取App的版本信息與PCH文件的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),對(duì)大家具有一定的參考價(jià)值,需要的朋友們下面來一起看看吧。
    2017-03-03
  • Swift能代替Objective-C嗎?

    Swift能代替Objective-C嗎?

    這是我在網(wǎng)上上看到的答案,復(fù)制粘貼過來和大家分享一下,因?yàn)槲液秃芏嗳艘粯雍荜P(guān)心Swift的出現(xiàn)對(duì)Mac開發(fā)的影響和對(duì)Objective-C的影響。
    2014-09-09
  • Swift仿選擇電影票的效果并實(shí)現(xiàn)無限/自動(dòng)輪播的方法

    Swift仿選擇電影票的效果并實(shí)現(xiàn)無限/自動(dòng)輪播的方法

    這篇文章主要給大家介紹了關(guān)于Swift仿選擇電影票的效果并實(shí)現(xiàn)無限/自動(dòng)輪播的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-08-08
  • 解析Swift語言面相對(duì)象編程中的繼承特性

    解析Swift語言面相對(duì)象編程中的繼承特性

    這篇文章主要介紹了解析Swift語言面相對(duì)象編程中的繼承特性,是Swift入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-11-11

最新評(píng)論

宜章县| 晋江市| 通河县| 海城市| 甘孜县| 神农架林区| 莆田市| 武宣县| 彰武县| 镇宁| 梁山县| 东丽区| 温宿县| 正安县| 体育| 泉州市| 河间市| 大化| 满城县| 柏乡县| 旺苍县| 浙江省| 赤水市| 房产| 玉门市| 琼海市| 东乌珠穆沁旗| 措美县| 博白县| 吴江市| 张家界市| 南阳市| 桐乡市| 沙雅县| 北川| 阿克陶县| 怀宁县| 垣曲县| 穆棱市| 峡江县| 马尔康县|