閉包是自包含的功能塊策严,可以在代碼中傳遞和使用。Swift中的閉包類似于C和Objective-C中的block,也類似于其他編程語言中的匿名函數(shù)。
閉包可以從定義它們的上下文中捕獲和存儲對任何常量和變量的引用唯卖。這就是所謂的封閉那些常數(shù)和變量粱玲。Swift為您處理捕獲的所有內(nèi)存管理。
函數(shù)中介紹的全局函數(shù)和嵌套函數(shù)實際上是閉包的特殊情況拜轨。閉包有三種形式:
- 全局函數(shù) 是 具有名稱且不捕獲任何值的閉包抽减。
- 嵌套函數(shù) 是 具有名稱的閉包,可以從其封閉函數(shù)中捕獲值橄碾。
- 閉包表達(dá)式是用輕量級語法編寫的未命名閉包卵沉,可以從它們周圍的上下文捕獲值。
Swift的閉包表達(dá)式有一個干凈法牲、清晰的風(fēng)格偎箫,通過優(yōu)化,在常見場景中鼓勵使用簡潔皆串、無雜亂的語法淹办。這些優(yōu)化包括:
- 從上下文推斷參數(shù)和返回值類型
- 單表達(dá)式閉包的隱式返回
- 簡寫參數(shù)名稱
- 尾部閉包的語法
Closure Expressions 閉包表達(dá)式
嵌套函數(shù)中引入的嵌套函數(shù)是一種方便的方法,可以將自包含的代碼塊命名和定義為較大函數(shù)的一部分恶复。但是怜森,有時編寫更短版本的類似函數(shù)的構(gòu)造而不使用完整的聲明和名稱是很有用的。當(dāng)您處理將函數(shù)作為一個或多個參數(shù)的函數(shù)或方法時谤牡,尤其如此副硅。
閉包表達(dá)式是一種用簡短的、有重點的語法編寫內(nèi)聯(lián)閉包的方法翅萤。閉包表達(dá)式提供了幾種語法優(yōu)化恐疲,可以在不丟失明確性或意圖的情況下以縮短的形式編寫閉包。下面的閉包表達(dá)式示例演示了這些優(yōu)化套么,它通過幾個迭代精煉了sort (by:)方法的一個示例培己,每個迭代都以更簡潔的方式表達(dá)相同的功能。
The Sorted Method 排序的方法
Swift的標(biāo)準(zhǔn)庫提供了一個名為ordered (by:)的方法胚泌,該方法根據(jù)提供的排序閉包的輸出對已知類型的值數(shù)組進行排序省咨。一旦完成排序過程,ordered (by:)方法返回與舊數(shù)組類型和大小相同的新數(shù)組玷室,其元素的排序順序正確零蓉。原始數(shù)組沒有被sort (by:)方法修改。
下面的閉包表達(dá)式示例使用ordered (by:)方法對字符串值數(shù)組按字母順序進行排序穷缤。這是要排序的初始數(shù)組:
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
sort (by:)方法接受一個閉包敌蜂,該閉包接受與數(shù)組內(nèi)容類型相同的兩個參數(shù),并返回一個Bool值津肛,以說明在對值進行排序后章喉,第一個值應(yīng)該出現(xiàn)在第二個值之前還是之后。如果第一個值出現(xiàn)在第二個值之前,排序閉包需要返回true囊陡,否則返回false芳绩。
這個例子是對字符串值數(shù)組進行排序,因此排序閉包需要是類型(String, String) -> Bool的函數(shù)撞反。
提供排序閉包的一種方法是編寫一個正確類型的普通函數(shù)妥色,并將其作為參數(shù)傳遞給sort (by:)方法:
func backward(_ s1: String, _ s2: String) -> Bool {
return s1 > s2
}
var reversedNames = names.sorted(by: backward)
// reversedNames is equal to ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
如果第一個字符串(s1)大于第二個字符串(s2),那么后退(::)函數(shù)將返回true遏片,表明在排序后的數(shù)組中s1應(yīng)該出現(xiàn)在s2之前嘹害。對于字符串中的字符,“greater than”的意思是“在字母表中出現(xiàn)的時間比”吮便。這意味著字母“B”大于字母“A”笔呀,字符串“Tom”大于字符串“Tim”。這給出了一個反字母排序髓需,“Barry”放在“Alex”前面许师,依此類推。
然而僚匆,這是一種相當(dāng)冗長的方式來編寫本質(zhì)上是一個單表達(dá)式函數(shù)(a > b)微渠。
Closure Expression Syntax 閉包表達(dá)式語法
閉包表達(dá)式語法的一般形式如下:
{ (parameters) -> return type in
statements
}
閉包表達(dá)式語法中的參數(shù)可以是in-out參數(shù),但不能有默認(rèn)值咧擂。如果您指定可變參數(shù)逞盆,則可以使用可變參數(shù)。元組還可以用作參數(shù)類型和返回類型松申。
下面的例子顯示了一個閉包表達(dá)式版本的倒向(::)函數(shù)從上面:
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})
注意云芦,這個內(nèi)聯(lián)閉包的參數(shù)和返回類型的聲明與back(::)函數(shù)的聲明相同。在這兩種情況下贸桶,它都被寫成(s1: String, s2: String) -> Bool舅逸。但是,對于內(nèi)聯(lián)閉包表達(dá)式刨啸,參數(shù)和返回類型是寫在大括號內(nèi)的堡赔,而不是寫在大括號外。
閉包主體的開始由in關(guān)鍵字引入设联。這個關(guān)鍵字表示閉包的參數(shù)和返回類型的定義已經(jīng)完成,閉包的主體即將開始灼捂。
因為閉包的主體很短离例,它甚至可以寫在一行:
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } )
這說明對sort (by:)方法的整體調(diào)用保持不變。一對圓括號仍然包含方法的整個參數(shù)悉稠。然而宫蛆,這個參數(shù)現(xiàn)在是一個內(nèi)聯(lián)閉包。
Inferring Type From Context 從上下文推斷類型
由于排序閉包作為參數(shù)傳遞給方法,Swift可以推斷其參數(shù)的類型和返回值的類型耀盗。sort (by:)方法是對字符串?dāng)?shù)組調(diào)用的想虎,因此它的參數(shù)必須是類型(String, String) -> Bool的函數(shù)。這意味著(String, String)和Bool類型不需要作為閉包表達(dá)式定義的一部分編寫叛拷。因為所有類型都可以推斷舌厨,所以返回箭頭(->)和參數(shù)名稱周圍的括號也可以省略:
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
在將閉包作為內(nèi)聯(lián)閉包表達(dá)式傳遞給函數(shù)或方法時,總是可以推斷參數(shù)類型和返回類型忿薇。因此裙椭,當(dāng)閉包用作函數(shù)或方法參數(shù)時,您永遠(yuǎn)不需要以最完整的形式編寫內(nèi)聯(lián)閉包署浩。
盡管如此揉燃,如果您愿意,您仍然可以使類型顯式筋栋,如果這樣做可以避免代碼讀者產(chǎn)生歧義炊汤,那么我們鼓勵您這樣做。sorted(by:) 方法,閉包的目的是明確排序發(fā)生的事實,它是安全的假設(shè)讀者關(guān)閉可能會使用字符串值,因為它是協(xié)助一個字符串?dāng)?shù)組的排序弊攘。
Implicit Returns from Single-Expression Closures 單表達(dá)式閉包的隱式返回
單表達(dá)式閉包可以隱式地返回其單表達(dá)式的結(jié)果婿崭,方法是在聲明中省略return關(guān)鍵字,就像在前一個例子的這個版本中一樣:
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
這里肴颊,sort (by:)方法參數(shù)的函數(shù)類型清楚地表明氓栈,閉包必須返回Bool值。因為閉包的主體包含一個返回Bool值的表達(dá)式(s1 > s2)婿着,所以沒有歧義授瘦,return關(guān)鍵字可以省略。
Shorthand Argument Names 簡寫參數(shù)名稱
Swift自動為內(nèi)聯(lián)閉包提供簡寫參數(shù)名竟宋,可以使用1、$2等名稱引用閉包參數(shù)的值徒欣。
如果在閉包表達(dá)式中使用這些簡寫參數(shù)名蜗字,則可以從閉包的定義中省略閉包的參數(shù)列表,并且將從預(yù)期的函數(shù)類型推斷出簡寫參數(shù)名的數(shù)量和類型挪捕。in關(guān)鍵字也可以省略粗梭,因為閉包表達(dá)式完全由它的主體組成:
reversedNames = names.sorted(by: { $0 > $1 } )
這里级零,1引用閉包的第一個和第二個字符串參數(shù)。
Operator Methods 操作方法
實際上,有一種更短的方法來編寫上面的閉包表達(dá)式鉴嗤。Swift的String類型將其特定于String的greater-than運算符(>)實現(xiàn)定義為一個方法斩启,該方法有兩個String類型的參數(shù),并返回一個Bool類型的值兔簇。這與sort (by:)方法所需的方法類型完全匹配荣挨。因此,你可以簡單地傳入大于運算符此虑,Swift會推斷你想使用它的字符串特定的實現(xiàn):
reversedNames = names.sorted(by: >)
Trailing Closures 尾部閉包
如果需要將閉包表達(dá)式作為函數(shù)的最后一個參數(shù)傳遞給函數(shù)口锭,并且閉包表達(dá)式很長朦前,那么將它寫成尾部閉包是很有用的鹃操。在函數(shù)調(diào)用的括號后面寫一個尾部閉包荆隘,即使它仍然是函數(shù)的參數(shù)。當(dāng)使用尾部閉包語法時晶渠,不會將閉包的參數(shù)標(biāo)簽作為函數(shù)調(diào)用的一部分編寫燃观。
func someFunctionThatTakesAClosure(closure: () -> Void) {
// function body goes here
}
// Here's how you call this function without using a trailing closure:
someFunctionThatTakesAClosure(closure: {
// closure's body goes here
})
// Here's how you call this function with a trailing closure instead:
someFunctionThatTakesAClosure() {
// trailing closure's body goes here
}
上面的閉包表達(dá)式語法部分中的字符串排序閉包可以在sort (by:)方法的括號外寫成尾部閉包:
reversedNames = names.sorted() { $0 > $1 }
如果一個閉包表達(dá)式作為函數(shù)或方法的唯一參數(shù)提供缆毁,并且您將該表達(dá)式作為尾部閉包提供,那么在調(diào)用函數(shù)時颁督,您不需要在函數(shù)或方法的名稱后面加上一對圓括號():
reversedNames = names.sorted { $0 > $1 }
當(dāng)閉包足夠長,無法在一行中內(nèi)聯(lián)編寫時适篙,尾隨閉包最有用箫爷。例如虎锚,Swift的數(shù)組類型有一個map(_:)方法,該方法將閉包表達(dá)式作為其單個參數(shù)效斑。對數(shù)組中的每個項調(diào)用閉包一次柱徙,并為該項返回一個可選映射值(可能是其他類型的)。映射的性質(zhì)和返回值的類型由閉包指定敌完。
將提供的閉包應(yīng)用于每個數(shù)組元素后羊初,map(_:)方法返回一個包含所有新映射值的新數(shù)組长赞,其順序與原始數(shù)組中的對應(yīng)值相同。
下面介紹如何使用帶有結(jié)尾閉包的map(_:)方法將Int值數(shù)組轉(zhuǎn)換為字符串值數(shù)組脯颜。數(shù)組[16,58,510]用于創(chuàng)建新數(shù)組["OneSix"贩据, "FiveEight", "FiveOneZero"]:
let digitNames = [
0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four",
5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let numbers = [16, 58, 510]
上面的代碼創(chuàng)建了一個映射字典讼庇,其中包含整數(shù)位數(shù)和它們的英文名稱之間的映射蠕啄。它還定義了一個整數(shù)數(shù)組戈锻,準(zhǔn)備轉(zhuǎn)換為字符串。
現(xiàn)在可以使用numbers數(shù)組創(chuàng)建字符串值數(shù)組哈街,方法是將閉包表達(dá)式作為尾閉包傳遞給數(shù)組的map(_:)方法:
let strings = numbers.map { (number) -> String in
var number = number
var output = ""
repeat {
output = digitNames[number % 10]! + output
number /= 10
} while number > 0
return output
}
// strings is inferred to be of type [String]
// its value is ["OneSix", "FiveEight", "FiveOneZero"]
map(_:)方法為數(shù)組中的每個項調(diào)用一次閉包表達(dá)式骚秦。您不需要指定閉包的輸入?yún)?shù)number的類型,因為類型可以從要映射的數(shù)組中的值推斷出來硬梁。
在本例中胞得,變量number由閉包的number參數(shù)值初始化,以便可以在閉包主體內(nèi)修改該值跃巡。(函數(shù)和閉包的參數(shù)總是常量牧愁。)閉包表達(dá)式還指定字符串的返回類型递宅,以指示將存儲在映射輸出數(shù)組中的類型。
每次調(diào)用閉包表達(dá)式時烘绽,它都會構(gòu)建一個名為output的字符串俐填。它使用余數(shù)運算符(數(shù)字% 10)計算數(shù)字的最后一位,并使用該數(shù)字在digitNames字典中查找適當(dāng)?shù)淖址甸堋i]包可用于創(chuàng)建任何大于零的整數(shù)的字符串表示形式驶悟。
對digitNames dictionary下標(biāo)的調(diào)用后面跟著一個感嘆號(!)痕鳍,因為dictionary下標(biāo)返回一個可選值,表示如果鍵不存在熊响,字典查找可能會失敗诗赌。在上面的示例中,可以保證number % 10始終是digitNames字典的有效下標(biāo)鍵洪碳,因此使用感嘆號強制打開存儲在下標(biāo)的可選返回值中的字符串值偶宫。
從digitNames字典檢索到的字符串被添加到輸出的前面环鲤,從而有效地構(gòu)建了數(shù)字的字符串版本。(表達(dá)式% 10的值為6表示16,8表示58,0表示510吵冒。)
數(shù)字變量然后除以10痹栖。因為它是整數(shù)瞭空,所以在除法的時候是四舍五入的,所以16變成1,58變成5,510變成51南捂。
這個過程一直重復(fù)旧找,直到number等于0钮蛛,此時閉包返回輸出字符串,map(_:)方法將輸出字符串添加到輸出數(shù)組中岭辣。
在上面的例子中甸饱,使用尾隨閉包語法巧妙地將閉包的功能封裝在閉包支持的函數(shù)之后柜候,而不需要將整個閉包封裝在map(_:)方法的外括號中。
Capturing Values 捕捉值
閉包可以從定義它的周圍上下文捕獲常量和變量鹦肿。然后辅柴,閉包可以從其主體中引用和修改這些常量和變量的值,即使定義常量和變量的原始范圍不再存在涣旨。
在Swift中霹陡,能夠捕獲值的閉包的最簡單形式是嵌套函數(shù),它寫在另一個函數(shù)的主體中攒霹。嵌套函數(shù)可以捕獲外部函數(shù)的任何參數(shù)浆洗,也可以捕獲外部函數(shù)中定義的任何常量和變量伏社。
下面是一個名為makeIncrementer的函數(shù)示例,它包含一個名為incrementer的嵌套函數(shù)速妖。函數(shù)的作用是:從上下文獲取兩個值runningTotal和amount第焰。獲取這些值之后,makeIncrementer返回incrementer作為一個閉包杀赢,每次調(diào)用runningTotal時脂崔,它都會按數(shù)量遞增梧喷。
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
makeIncrementer的返回類型是()-> Int.這意味著它返回一個函數(shù)铺敌,而不是一個簡單的值。它返回的函數(shù)沒有參數(shù)产弹,每次調(diào)用它都會返回一個Int值痰哨。要了解函數(shù)如何返回其他函數(shù)斤斧,請將函數(shù)類型視為返回類型。
函數(shù)的作用是:定義一個名為runningTotal的整數(shù)變量蕊连,用于存儲將返回的遞增程序的當(dāng)前運行總數(shù)锐秦。此變量的初始值為0酱床。
函數(shù)makeIncrementer(forIncrement:)有一個Int參數(shù)趟佃,參數(shù)標(biāo)簽為forIncrement闲昭,參數(shù)名稱為amount。傳遞給該參數(shù)的參數(shù)值指定每次調(diào)用返回的incrementer函數(shù)時應(yīng)該增加多少runningTotal鸯绿。makeIncrementer函數(shù)定義了一個名為incrementer的嵌套函數(shù)簸淀,該函數(shù)執(zhí)行實際的遞增租幕。這個函數(shù)簡單地將amount添加到runningTotal中,并返回結(jié)果男窟。
當(dāng)單獨考慮時贾富,嵌套incrementer()函數(shù)可能看起來不尋常:
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
incrementer()函數(shù)沒有任何參數(shù)颤枪,但是它在函數(shù)體中引用runningTotal和amount汇鞭。它通過從周圍函數(shù)獲取對runningTotal和amount的引用庸追,并在自己的函數(shù)體中使用它們來實現(xiàn)這一點淡溯。通過引用捕獲確保在調(diào)用makeIncrementer結(jié)束時runningTotal和amount不會消失簿训,還確保在下一次調(diào)用incrementer函數(shù)時runningTotal是可用的。
作為一種優(yōu)化膘侮,Swift可以捕獲并存儲一個值的副本琼了,如果該值沒有被閉包更改夫晌,并且在創(chuàng)建閉包之后該值沒有發(fā)生更改晓淀。
Swift還處理所有涉及處理不再需要的變量的內(nèi)存管理。
下面是makeIncrementer的一個例子:
let incrementByTen = makeIncrementer(forIncrement: 10)
本例設(shè)置了一個名為incrementby10的常量燥爷,以引用一個incrementer函數(shù)懦窘,該函數(shù)每次調(diào)用runningTotal變量時都會向其添加10奶赠。多次調(diào)用該函數(shù)顯示了以下行為:
incrementByTen()
// returns a value of 10
incrementByTen()
// returns a value of 20
incrementByTen()
// returns a value of 30
如果您創(chuàng)建第二個增量器毅戈,它將有自己的存儲引用到一個新的、獨立的runningTotal變量:
let incrementBySeven = makeIncrementer(forIncrement: 7)
incrementBySeven()
// returns a value of 7
再次調(diào)用原始incrementer (incrementByTen)將繼續(xù)遞增它自己的runningTotal變量赘理,并且不影響incrementBySeven捕獲的變量:
incrementByTen()
// returns a value of 40
注意:如果您將閉包分配給類實例的屬性商模,并且閉包通過引用實例或其成員來捕獲該實例,那么您將在閉包和實例之間創(chuàng)建一個強引用循環(huán)响疚。Swift使用捕獲列表來打破這些強引用循環(huán)瞪醋。有關(guān)更多信息银受,請參見閉包的強引用循環(huán)。
Closures Are Reference Types 閉包是引用類型
在上面的例子中咕幻,incrementBySeven和incrementbyTen是常量肄程,但是這些常量引用的閉包仍然能夠增加它們捕獲的運行中的總變量确丢。這是因為函數(shù)和閉包是引用類型鲜侥。
當(dāng)你將一個函數(shù)或閉包賦值給一個常量或變量時诸典,你實際上是將這個常量或變量設(shè)置為函數(shù)或閉包的引用狐粱。在上面的例子中,是閉包的選擇互墓,incrementbyTen指的是它是常量篡撵,而不是閉包本身的內(nèi)容豆挽。
這也意味著帮哈,如果您將一個閉包分配給兩個不同的常量或變量,那么這兩個常量或變量都指向同一個閉包咖刃。
let alsoIncrementByTen = incrementByTen
alsoIncrementByTen()
// returns a value of 50
incrementByTen()
// returns a value of 60
上面的例子顯示調(diào)用alsoIncrementByTen與調(diào)用incrementByTen 是相同的嚎杨。因為它們都引用相同的閉包,所以它們都遞增并返回相同的運行總數(shù)翠胰。
Escaping Closures 脫離閉包
當(dāng)閉包作為參數(shù)傳遞給函數(shù)時自脯,閉包被稱為轉(zhuǎn)義函數(shù)膏潮,但是在函數(shù)返回后調(diào)用閉包。當(dāng)您聲明一個函數(shù)轻纪,該函數(shù)將閉包作為其參數(shù)之一時刻帚,您可以在參數(shù)的類型之前編寫@ escape崇众,以指示允許該閉包進行轉(zhuǎn)義航厚。
閉包可以轉(zhuǎn)義的一種方法是存儲在函數(shù)外部定義的變量中幔睬。例如,許多啟動異步操作的函數(shù)都將閉包參數(shù)作為完成處理程序赦抖。函數(shù)在啟動操作后返回摹芙,但是直到操作完成才調(diào)用閉包——閉包需要轉(zhuǎn)義宛瞄,稍后調(diào)用。例如:
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
someFunctionWithEscapingClosure(_:)函數(shù)接受閉包作為參數(shù)蝴簇,并將其添加到函數(shù)外部聲明的數(shù)組中匆帚。如果不使用@ escape標(biāo)記此函數(shù)的參數(shù)吸重,將會得到編譯時錯誤嚎幸。
用@ escape標(biāo)記閉包意味著必須在閉包中顯式引用self。例如骑疆,在下面的代碼中,傳遞給someFunctionWithEscapingClosure(:)的閉包是一個轉(zhuǎn)義閉包诈火,這意味著它需要顯式地引用self柄瑰。相反剪况,傳遞給someFunctionWithNonescapingClosure(:)的閉包是一個不可轉(zhuǎn)義的閉包译断,這意味著它可以隱式地引用self或悲。
func someFunctionWithNonescapingClosure(closure: () -> Void) {
closure()
}
class SomeClass {
var x = 10
func doSomething() {
someFunctionWithEscapingClosure { self.x = 100 }
someFunctionWithNonescapingClosure { x = 200 }
}
}
let instance = SomeClass()
instance.doSomething()
print(instance.x)
// Prints "200"
completionHandlers.first?()
print(instance.x)
// Prints "100"
Autoclosures 自動閉包
自動閉包是一個自動創(chuàng)建的閉包巡语,用于包裝作為參數(shù)傳遞給函數(shù)的表達(dá)式男公。它不接受任何參數(shù),當(dāng)它被調(diào)用時拥知,它返回封裝在其中的表達(dá)式的值碎赢。這種語法上的便利使您可以通過編寫普通表達(dá)式而不是顯式閉包來省略函數(shù)參數(shù)周圍的大括號肮塞。
調(diào)用帶有自動閉包的函數(shù)是很常見的枕赵,但是實現(xiàn)這種函數(shù)并不常見。例如替梨,assert(condition:message:file:line:)函數(shù)的條件和消息參數(shù)采用自動閉包;它的條件參數(shù)僅在調(diào)試構(gòu)建中求值副瀑,而消息參數(shù)僅在條件為false時求值。
自動閉包允許延遲計算盹沈,因為在調(diào)用閉包之前,內(nèi)部的代碼不會運行均抽。延遲計算對于有副作用或計算開銷大的代碼很有用其掂,因為它允許您控制何時計算代碼款熬。下面的代碼顯示了閉包如何延遲計算。
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)
// Prints "5"
let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)
// Prints "5"
print("Now serving \(customerProvider())!")
// Prints "Now serving Chris!"
print(customersInLine.count)
// Prints "4"
即使customersInLine數(shù)組的第一個元素被閉包內(nèi)的代碼刪除贤牛,數(shù)組元素在實際調(diào)用閉包之前也不會被刪除惋鹅。如果從未調(diào)用閉包,則閉包內(nèi)的表達(dá)式將永遠(yuǎn)不會被求值殉簸,這意味著數(shù)組元素永遠(yuǎn)不會被刪除闰集。注意customerProvider的類型不是String沽讹,而是()-> String—一個沒有返回字符串參數(shù)的函數(shù)。
當(dāng)將閉包作為參數(shù)傳遞給函數(shù)時返十,會得到相同的延遲求值行為妥泉。
// customersInLine is ["Alex", "Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: { customersInLine.remove(at: 0) } )
// Prints "Now serving Alex!"
上面清單中的service (customer:)函數(shù)接受一個顯式閉包洞坑,該閉包返回客戶的名稱盲链。下面的service (customer:)版本執(zhí)行相同的操作,但是它沒有采用顯式的閉包迟杂,而是使用@autoclosure屬性標(biāo)記參數(shù)的類型來使用自動閉包」粽矗現(xiàn)在,您可以調(diào)用該函數(shù)排拷,就好像它接受一個字符串參數(shù)而不是閉包一樣侧漓。參數(shù)將自動轉(zhuǎn)換為閉包,因為customerProvider參數(shù)的類型由@autoclosure屬性標(biāo)記监氢。
// customersInLine is ["Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: @autoclosure () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: customersInLine.remove(at: 0))
// Prints "Now serving Ewa!"
注意:過度使用自動閉包會使代碼難以理解布蔗。上下文和函數(shù)名應(yīng)該清楚地表明,正在延遲計算浪腐。
如果您想要一個允許轉(zhuǎn)義的自動閉包纵揍,請同時使用@autoclosure和@escaping屬性。
// customersInLine is ["Barry", "Daniella"]
var customerProviders: [() -> String] = []
func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> String) {
customerProviders.append(customerProvider)
}
collectCustomerProviders(customersInLine.remove(at: 0))
collectCustomerProviders(customersInLine.remove(at: 0))
print("Collected \(customerProviders.count) closures.")
// Prints "Collected 2 closures."
for customerProvider in customerProviders {
print("Now serving \(customerProvider())!")
}
// Prints "Now serving Barry!"
// Prints "Now serving Daniella!"
在上面的代碼中议街,collectCustomerProviders(_:)函數(shù)沒有調(diào)用傳遞給它的閉包作為其customerProvider參數(shù)泽谨,而是將閉包附加到customerProviders數(shù)組中。數(shù)組聲明在函數(shù)作用域之外特漩,這意味著可以在函數(shù)返回后執(zhí)行數(shù)組中的閉包吧雹。因此,必須允許customerProvider參數(shù)的值轉(zhuǎn)義函數(shù)的作用域涂身。