iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 13
0

就算不舒服還是要當鐵人、發鐵人文。
頭痛時看這些有點難理解的東西頭又痛了 QAQ (閉包是什麼可以吃嗎,到底跑去哪了

尾隨閉包 (Trailing Closures)

如果你需要一段的閉包表達式作為 function 最後一個參數傳遞給 function 使用,那使用尾隨閉包是一個非常好的方式,同時也能增加他的可讀性,尾隨閉包是一個被書寫在 function 形式參數的括號後面的閉包表達式。

https://ithelp.ithome.com.tw/upload/images/20180101/20107701rDXjwpjCw9.png

*如果閉包表達式被用作 function 唯一的實際參數並且你把閉包表達式用作尾隨閉包,那麼調用這個函數的時候你就不需要在函數的名字後面加上一個圓括號 ( ) *

下列我們用 map() 方法來進行一個 Int -> String 的例子,map()方法,map()簡單來說就是通過接收一個function 作為傳入參數,對數組中每個元素進行變換得到新的結果值。只需要提供 A 和 B 的對應關係,就能將數組[A]變換到新數組[B]。

https://ithelp.ithome.com.tw/upload/images/20180101/20107701UR9YVFTCdu.png

現在可以使用 numbers 數組創建一個String值的數組,通過將閉包表達式作為尾隨閉包傳遞給數組的 map() 方法,因為 map()方法是為數組中的每個項目調用一次閉包表達式,所以不需要指定閉包的輸入參數類型,因為可以從要對應的數組中的值中推斷出該類型。我們不需要在 map 後加上(),因為 map() 方法中只有一個形式參數。

這個栗子中,變數 number 是以閉包的 number 形式參數進行初始化,這樣它就可以在閉包結構內部直接被修改。(函數和閉包的形式參數是常量。)閉包表達式指定返回 String 類型,用來存儲對應值的 output 也為 String 類型。

然後我們程式碼抓取值的方式如下,舉例:1234,他會先抓出除以10之後剩下的餘數,也就是4,並在 iconNum 中找尋他對應的值,並加在變數 output 的前方,最後再將 1234 除以 10,我們會得到 123,再重複先前操作,進而得到 3 , 2 ,1 直到 number 不在大於 0 為止,再進行其他值的運算。


捕獲值 (Capturing Values)

閉包能夠從上下文中捕獲已被定義的常數和變數。即使定義常數和變數的原始區域不存在,閉包依然可以引用並修改其正文中那些常數和變數的值。在Swift 中,一個能夠捕獲值的閉包最簡單的模型是內嵌函數,即被書寫在另一個函數的內部。內嵌函數可以捕獲任何外部函數的參數,也可以捕獲外部函數中定義的任何常數和變數。

https://ithelp.ithome.com.tw/upload/images/20180101/20107701kMdsGK7q26.png

這邊我們建立一個叫 addFunc 的 function,然後其中有一個叫做 add 的內嵌函數,這個內嵌函數可以補獲我們上下文中的 total 以及 number 的值,捕獲這些值之後,透過 addFunc 將我們的 add 作為一個閉包返回,每次使用 add 時,就會把 number 內的值加到我們 total 中。

我們使用add這個內嵌函數來當 addFunc 的返回值,所以 addFunc 的返回類型是 ( ) -> Int ,表示這個函數沒有返回任何形式參數,每調用一次就返回一個 Int 的值。其中我們的 add 這個內嵌函數是沒有任何形式參數,都是通過捕獲主函數的 total 和 number 把它們內嵌在自身函數內部供使用。當使用 addFunc 結束時通過引用捕獲來確保不會消失,並確保了在下次再次調用 add 時, total 將繼續增加。

https://ithelp.ithome.com.tw/upload/images/20180101/20107701PO84LWe2oX.png

宣告常數到 addFunc 之後你便可以自定義 addFunc 內的參數值,讓每次使用 addFunc 的時候能根據他的參數值增加。如果你宣告了兩個調用 addFunc 的常數,他們個字會有自己一個獨立儲存的 total 變數可以使用,並不會互相影響。


逃逸閉包 (Escaping Closures)

當閉包被作為參數傳遞給函數時,我們就說這個閉包逃逸了,因為它可以在函數返回之後被調用。當你聲明接受一個閉包作為其參數的函數時,你可以在參數的類型之前寫 @escaping 來明確表示允許閉包逃避。

閉包可以逃逸的一種方法是被儲存在定義於函數外的變量裡。舉個例子:很多函數接收閉包實際參數來作為啟動不同步的程序的操作。該函數在開始操作之後返回,但是直到操作完成才會調用閉包 - 閉包需要轉義,稍後調用。
https://ithelp.ithome.com.tw/upload/images/20180101/20107701MNO1a9cJEG.png

函數 someFunctionWithEscapingClosure 會接收一個閉包作為參數並且增加它到聲明在 func 外部的數組裡。如果你不標記函數的形式參數為 @ escaping,你就會遇到編譯時錯誤。

讓閉包逃逸意味著你必須在閉包中顯式地引用 self 。相反,假如閉包是非逃逸閉包,也就是說它可以隱式地引用 self,也就是不用特別使用 self。註解說明每段的程式碼的功能

https://ithelp.ithome.com.tw/upload/images/20180101/20107701htT8nGLn0l.png


自動閉包 (Auto Closure)

自動閉包是一種自動創建的用來把作為參數傳遞給函數的表達式打包的閉包。它不接受任何參數,並且當它被調用時,它會返回內部打包的表達式的值。自動閉包允許你延遲處理,因此閉包內部的代碼直到你使用它的時候才會運行。對於有副作用或者佔用資源的程式碼來說很有用,因為它可以允許你控制代碼何時才進行求值。

https://ithelp.ithome.com.tw/upload/images/20180101/20107701PkeOIMKj2x.png

這段程式碼中我們看得到,我們刪除了 letterArray 中的第一個元素,但我沒還沒調用 arrayRemove 時,他的陣列數量依然還是 6 ,也就是延遲求值,如果閉包永遠不被調用,那麼閉包裡邊的表達式就永遠不會求值。

當你傳一個閉包作為參數到函數的時候,你會得到與延遲處理相同的行為。
https://ithelp.ithome.com.tw/upload/images/20180101/20107701Fw4EsDQnLR.png

我們可以通過 @autoclosure 標記它的參數使用了自動閉包。現在你可以調用函數就像它接收了一個 String 參數而不是閉包。參數自動地轉換為閉包,因為 arrayRemove 參數的類型被標記為 @ autoclosure 自動閉包。

https://ithelp.ithome.com.tw/upload/images/20180101/20107701J82jsbi6he.png


上一篇
Day-12 Swift 語法(8) - Closures
下一篇
Day-14 Swift 語法(10) - 多種用途的 Enumerations
系列文
Swift 菜鳥的30天30

2 則留言

0
ellstang
iT邦新手 5 級 ‧ 2018-01-01 22:11:06

程式碼太小了啦~~~XD

大姐我會好好檢討的,我一定是醉了

0
陳董粉絲
iT邦新手 5 級 ‧ 2018-01-08 17:55:36

swift裡說的 AutoClosure(@autoclosure) 功用應該就你最後一段描述的 美化函數參數中有閉包寫的方式而已,你說的延遲處理就是閉包的使用特性
就我的觀察啦~

我要留言

立即登入留言