<kbd id="5sdj3"></kbd>
<th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>

    圖解 Swift async/await

    共 4358字,需瀏覽 9分鐘

     ·

    2021-07-02 01:00

    ????關(guān)注后回復(fù) “進(jìn)群” ,拉你進(jìn)程序員交流群????


    作者丨林克
    來(lái)源丨知識(shí)小集(ID:zsxjtip)



    Swift 開(kāi)發(fā)者終于在 WWDC21 中盼來(lái)了 async/await。作為現(xiàn)代編程語(yǔ)言的標(biāo)志之一,async/await 可以讓我們像編寫(xiě)常規(guī)代碼一樣,輕松地編寫(xiě)異步代碼,這樣能更直觀且更安全地表達(dá)我們的思路。同時(shí),async/await 是整個(gè) Swift 結(jié)構(gòu)化并發(fā)的基礎(chǔ),所以我們從這個(gè) Session 開(kāi)始,一起來(lái)探索 Swift 新的并發(fā)框架。


    加載縮略圖步驟



    這個(gè) Session 通過(guò)加載縮略圖片為我們演示了 async/await 的使用。加載縮略圖片分為以下幾個(gè)步驟:

    ? 從 URL 字符串創(chuàng)建一個(gè) URLRequest 對(duì)象;
    ? URLSession 的 dataTask(with:completion:) 方法獲取要請(qǐng)求圖片數(shù)據(jù);
    ? UIImage(data:) 從圖片數(shù)據(jù)中創(chuàng)建一個(gè)圖像;
    ? UIImage 的 prepareThumbnail 方法從原始圖像中渲染一個(gè)縮略圖

    這些操作的每一步都依賴于前一個(gè)步驟的結(jié)果,所以必須按順序執(zhí)行。在這四步操作中,第二步和第四步會(huì)比較費(fèi)時(shí),所以這兩步操作一般通過(guò)異步調(diào)用來(lái)完成。


    原始版本代碼


    原始的調(diào)用方式如上代碼所示,這里定義了一個(gè) fetchThumbnail 函數(shù),其中有一個(gè) completion 參數(shù),用于將輸出返回給調(diào)用者。

    fetchThumbnail 函數(shù)體中規(guī)中舉地按以上步驟來(lái)完成任務(wù)

    ? 調(diào)用 thumbnailURLRequest,該操作是同步的;
    ? 調(diào)用 URLSession 的 dataTask(with:completion:),這是個(gè)異步操作,需要調(diào)用 resume() 方法啟動(dòng)異步工作;fetchThumbnail 返回執(zhí)行其它任務(wù)
    ? 在 dataTask(with:completion:) 的完成處理程序中,處理獲取數(shù)據(jù)后的操作;
    ? 正常獲取數(shù)據(jù)的情況下,通過(guò) UIImage(data:) 來(lái)創(chuàng)建一個(gè)圖像,這個(gè)操作是同步的;
    ? 調(diào)用 UIImage 對(duì)象的 prepareThumbnail 方法生成縮略圖,該方法也是個(gè)異步方法;

    這里有兩個(gè)明顯的問(wèn)題:

    ? 必須時(shí)刻注意在哪里需要調(diào)用 completion(這里就有 5 處),如果忘記調(diào)用 completion 來(lái)通知調(diào)用者失?。⊿wift 不能保證強(qiáng)制執(zhí)行 completion),則可能導(dǎo)致流程異常;
    ? 這里大約 20 行的代碼,看似是按流程來(lái)走,但層層嵌套會(huì)讓代碼顯示更加晦澀(所謂的回調(diào)地獄);

    有一些現(xiàn)成的改進(jìn)方案,但效果并不理想:

    ? completion 的入?yún)⒖梢允褂脴?biāo)準(zhǔn)庫(kù)的 Result,會(huì)更安全一點(diǎn),但也只是一點(diǎn)點(diǎn);
    ? 類(lèi) future 方法,但代碼沒(méi)有更簡(jiǎn)潔和安全

    現(xiàn)在,必須祭出 async/await 了。


    async/await 版本代碼

    async/await 版本代碼如上所示。

    函數(shù)簽名有幾處明顯的變化:

    ? 入?yún)⒉辉俚?completion 處理程序;
    ? 返回值是 UIImage 類(lèi)型,表示函數(shù)返回的縮略圖,同時(shí)通過(guò)關(guān)鍵字 throws 標(biāo)識(shí)可以拋出一個(gè)異常;
    ? 在 throws 前面添加了關(guān)鍵字 async,表明這是一個(gè)異步函數(shù)(注意:如果沒(méi)有 throws 關(guān)鍵字,則 async 直接放在 -> 前面)

    fetchThumbnail 的實(shí)現(xiàn)更加簡(jiǎn)潔明了:

    ? 調(diào)用依然從 thumbnailURLRequest 開(kāi)始,該操作是同步的,阻塞線程;
    ? 調(diào)用 URLSession 的 data(for:) 開(kāi)始下載數(shù)據(jù),這里有幾個(gè)變化

       ? 使用 await 標(biāo)記方法調(diào)用,表明這是一個(gè)異步操作;如果一個(gè)表達(dá)式里面有多個(gè)異步函數(shù)調(diào)用,則只需要寫(xiě)一次 await
    ? data(for: ) 方法是可等待的,調(diào)用后,會(huì)掛起自己,解除線程阻塞;
    ? 使用 try 是因?yàn)?fetchThumbnail 被標(biāo)記為 throws,如果網(wǎng)絡(luò)請(qǐng)求有異步,則直接拋出異常;
    ? data(for:) 完成后,恢復(fù) fetchThumbnail,并將返回的數(shù)據(jù)及請(qǐng)求響應(yīng)賦值給 data 和 response,就像普通的賦值操作一樣;

    ? 判斷響應(yīng)是否有效,如果響應(yīng)碼不為 200,則拋出異步,這一步為同步操作;
    ? 正常獲取數(shù)據(jù)后,通過(guò) UIImage(data:) 來(lái)創(chuàng)建一個(gè)圖像,這個(gè)操作是同步的;
    ? 調(diào)用 UIImage 對(duì)象的 thumbnail 屬性生成縮略圖,這是個(gè)可等待的屬性(此處為非 SDK 內(nèi)置的屬性,而是自定義的屬性),這里同樣使用 await 來(lái)標(biāo)識(shí)異步操作;如果 thumbnail 失敗,則拋出一個(gè)異常;

    我們可以看到,短短的 6 行代碼就實(shí)現(xiàn)了上面大約 20 行代碼的功能,優(yōu)點(diǎn)顯而易見(jiàn):

    ? 更安全:整個(gè)過(guò)程能確保出錯(cuò)時(shí)拋出異常;
    ? 更簡(jiǎn)潔:避免的代碼的層層嵌套;
    ? 更能體現(xiàn)意圖:整個(gè)代碼基本是和我們預(yù)定的流程保持了一致;


    可等待屬性


    如上代碼,是上面使用的 thumbnail 屬性的實(shí)現(xiàn)。它是 UIImage 的一個(gè)擴(kuò)展屬性。

    這里需要注意幾點(diǎn):

    ? 異步屬性必須是只讀的,可寫(xiě)屬性不能聲明為異步屬性;
    ? 異步屬性需要有一個(gè)明確的 getter,async 關(guān)鍵字位于 get 后;
    ? 從 Swift 5.5 開(kāi)始,getter 也可以拋出異常,如果同時(shí)是異步的,則 async 關(guān)鍵字位于 throws 前面;
    ? await 可用于屬性 body 中的表達(dá)式,以表明操作的異步性;


    調(diào)用流程對(duì)比


    普通函數(shù)的調(diào)用流程如上圖所示:

    ? 調(diào)用函數(shù);
    ? 函數(shù)獲取線程的控制權(quán),并完全占用該線程;
    ? 函數(shù)執(zhí)行完成返回或者拋出錯(cuò)誤,將控制權(quán)交還調(diào)用方

    這里普通函數(shù)放棄線程控制權(quán)的唯一方式就是執(zhí)行完成。


    而異步函數(shù)的調(diào)用流程則如上圖所示:

    ? 調(diào)用函數(shù);
    ? 函數(shù)獲得線程控制權(quán);
    ? 函數(shù)運(yùn)行后,掛起,同時(shí)放棄對(duì)線程的控制,并將控制權(quán)交給系統(tǒng),系統(tǒng)可自由支配該線程;
    ? 系統(tǒng)確定何時(shí)恢復(fù)函數(shù);
    ? 函數(shù)恢復(fù)后重新獲得控制權(quán),并繼續(xù)工作;
    ? 函數(shù)執(zhí)行完成或拋出異常后,返回調(diào)用方,將控制權(quán)交還給調(diào)用方

    這里需要注意幾點(diǎn):

    ? 一個(gè)異步函數(shù)掛起時(shí),也會(huì)掛起它的調(diào)用者,所以調(diào)用者也必須是異步的;
    ? 異步函數(shù)可以多次掛起,就像上面的 fetchThumbnail 方法一樣使用了兩個(gè) await 關(guān)鍵字;
    ? 異步函數(shù)掛起時(shí),不會(huì)阻塞線程;
    ? 異步函數(shù)可能會(huì)在一個(gè)完全不同的線程上恢復(fù);
    ? async 函數(shù)并不一定會(huì)掛起;


    單元測(cè)試


    以往,我們想要對(duì)網(wǎng)絡(luò)請(qǐng)求任務(wù)做一些單元測(cè)試時(shí),需要借助 XCTestExpectation 這個(gè)類(lèi)。


    而有了 async/await 后,異步函數(shù)的單元測(cè)試更加簡(jiǎn)單了。


    異步任務(wù)


    我們上面提到,異步函數(shù)的調(diào)用者也需要是異步函數(shù)。但有些情況下,確實(shí)需要在同步方法中調(diào)用異步函數(shù),這時(shí)就需要用到異步任務(wù)功能了。

    即如上代碼,通過(guò) async 閉包將任務(wù)打包,以執(zhí)行異步操作。


    代碼遷移

    對(duì)于老代碼,我們更關(guān)注的是如何更好地使用上新特性。對(duì)于 async/await,Swift 團(tuán)隊(duì)也為我們做了很多準(zhǔn)備:


    ? 提供了大量的異步 API,以替代采用完成處理程序的 API

    ? 許多委托方法也有相應(yīng)的異步替代方法


    Continuation 模式

    Swift 是如何與系統(tǒng)協(xié)作,完成異步代碼的恢復(fù)呢。答案就是 Continuation 模式,方法的調(diào)用者等待函數(shù)調(diào)用的結(jié)果并提供一個(gè)閉包來(lái)指定下一步要做什么。當(dāng)函數(shù)調(diào)用完成時(shí),調(diào)用完成處理程序恢復(fù)調(diào)用者想要對(duì)結(jié)果執(zhí)行的操作。這種協(xié)同執(zhí)行正是 Swift 中異步函數(shù)的工作方式。

    為此,Swift 提供了 withCheckedThrowingContinuation 函數(shù)

    Suspends the current task, then calls the given closure with a checked throwing continuation for the current task.

    以及 CheckedContinuation 結(jié)構(gòu)體

    A mechanism to interface between synchronous and asynchronous code, logging correctness violations.

    通過(guò)這些結(jié)構(gòu)體和函數(shù),調(diào)用方可以訪問(wèn)可用于恢復(fù)掛起的異步函數(shù)的延續(xù)值。CheckedContinuation 結(jié)構(gòu)體還提供了多個(gè) resume 方法,用來(lái)回傳結(jié)果值。

    Continuation 提供了一種強(qiáng)大的方式來(lái)手動(dòng)控制異步函數(shù)的執(zhí)行,不過(guò)有一點(diǎn)需要記?。?br style="box-sizing: border-box;">

    resume 在每個(gè)代碼分支上必須且只能調(diào)用一次

    如果某個(gè)分支沒(méi)有調(diào)用 resume,異步調(diào)用將永遠(yuǎn)掛起;而如果某個(gè)分支調(diào)用了多次,則可能會(huì)破壞程序數(shù)據(jù)。這兩種情況 Swift 都會(huì)給出警告或錯(cuò)誤。


    小結(jié)

    以上就是對(duì)《Meet async/await in Swift》的整理,展示了 async/await 的使用及運(yùn)行機(jī)制,歡迎來(lái)到 Swift 新世界。

    -End-

    最近有一些小伙伴,讓我?guī)兔φ乙恍?nbsp;面試題 資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來(lái),可以說(shuō)是程序員面試必備!所有資料都整理到網(wǎng)盤(pán)了,歡迎下載!

    點(diǎn)擊??卡片,關(guān)注后回復(fù)【面試題】即可獲取

    在看點(diǎn)這里好文分享給更多人↓↓

    瀏覽 17
    點(diǎn)贊
    評(píng)論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報(bào)
    評(píng)論
    圖片
    表情
    推薦
    點(diǎn)贊
    評(píng)論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報(bào)

    <kbd id="5sdj3"></kbd>
    <th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>
    日韩一区二区三区免费播放 | 欧美肏屄 | 天堂中文在线资源视频 | 欧美无砖砖区免费 | 国产成人激情视频 |