創(chuàng)建基礎(chǔ)的閉包
Swift使我們可以像使用其他類型一樣使用函數(shù)望蜡,例如字符串和整數(shù)燃领。這意味著您可以創(chuàng)建一個(gè)函數(shù)并將其分配給一個(gè)變量聘惦,使用該變量調(diào)用該函數(shù)丸逸,甚至將該函數(shù)作為參數(shù)傳遞給其他函數(shù)蹋艺。
以這種方式使用的函數(shù)稱為閉包,盡管它們像函數(shù)一樣工作黄刚,但編寫方式有所不同捎谨。
讓我們從一個(gè)簡單的示例開始,該示例顯示一條消息:
let driving = {
print("I'm driving in my car")
}
這樣可以有效地創(chuàng)建一個(gè)沒有名稱的函數(shù)憔维,并將該函數(shù)分配給driving√尉龋現(xiàn)在,您可以driving()像調(diào)用常規(guī)函數(shù)一樣進(jìn)行調(diào)用业扒,如下所示:
driving()
在閉包中接受參數(shù)
創(chuàng)建閉包時(shí)检吆,它們沒有名稱或空格來寫入任何參數(shù)。這并不意味著它們不能接受參數(shù)程储,只是它們以不同的方式接受參數(shù):它們在大括號內(nèi)列出蹭沛。
要使閉包接受參數(shù),請?jiān)陂_括號后面的括號內(nèi)列出它們章鲤,然后寫下in
讓Swift知道閉包的主體已經(jīng)開始了
例如摊灭,我們可以創(chuàng)建一個(gè)接受地名字符串作為其唯一參數(shù)的閉包,如下所示:
let driving = { (place: String) in
print("I'm going to \(place) in my car")
}
函數(shù)和閉包之間的區(qū)別之一是败徊,在運(yùn)行閉包時(shí)不使用參數(shù)標(biāo)簽帚呼。因此,調(diào)用driving()
我們現(xiàn)在寫成這樣:
driving("London")
從閉包中返回值
閉包也可以返回值皱蹦,它們的寫法與參數(shù)類似:您可以在閉包內(nèi)直接在in關(guān)鍵字之前寫它們煤杀。
為了證明這一點(diǎn),我們將使用driving()
閉包并使其返回其值沪哺,而不是直接打印它怜珍。這是原始的:
let driving = { (place: String) in
print("I'm going to \(place) in my car")
}
我們希望閉包返回一個(gè)字符串而不是直接打印消息,因此我們需要在in
之前使用-> String
凤粗,然后像普通函數(shù)一樣使用return
:
let drivingWithReturn = { (place: String) -> String in
return "I'm going to \(place) in my car"
}
現(xiàn)在,我們可以運(yùn)行該閉包并打印其返回值:
let message = drivingWithReturn("London")
print(message)
閉包作為參數(shù)
因?yàn)殚]包可以像字符串和整數(shù)一樣使用,所以可以將它們傳遞給函數(shù)嫌拣。語法起初會讓您頭痛柔袁,因此我們將慢慢來。
首先异逐,這是我們的基本driving()
閉包
let driving = {
print("I'm driving in my car")
}
如果我們想將該閉包傳遞給函數(shù)捶索,以便可以在該函數(shù)內(nèi)部運(yùn)行,則可以將參數(shù)類型指定為() -> Void
灰瞻。這意味著“不接受任何參數(shù)腥例,然后返回Void
” – Swift所說的“什么都不做”。
因此酝润,我們可以編寫一個(gè)travel()
接受各種旅行行為的函數(shù)燎竖,并在此之前和之后打印一條消息:
func travel(action: () -> Void) {
print("I'm getting ready to go.")
action()
print("I arrived!")
}
我們現(xiàn)在可以使用driving
閉包來調(diào)用它,如下所示:
travel(action: driving)
尾隨閉包語法
如果函數(shù)的最后一個(gè)參數(shù)是閉包要销,則Swift可讓您使用稱為尾隨閉包語法的特殊語法构回。與其在閉包中作為參數(shù)傳遞,不如在括號內(nèi)的函數(shù)之后直接傳遞它疏咐。
為了證明這一點(diǎn)纤掸,這又是我們的travel()
功能。它接受action閉包浑塞,以便可以在兩個(gè)print()
調(diào)用之間運(yùn)行:
func travel(action: () -> Void) {
print("I'm getting ready to go.")
action()
print("I arrived!")
}
因?yàn)樗淖詈笠粋€(gè)參數(shù)是閉包借跪,所以我們可以使用尾隨閉包語法進(jìn)行調(diào)用travel()
,如下所示:
travel() {
print("I'm driving in my car")
}
實(shí)際上酌壕,由于沒有任何其他參數(shù)掏愁,我們可以完全消除括號:
travel {
print("I'm driving in my car")
}
此時(shí)會輸出如下內(nèi)容:
I'm getting ready to go.
I'm driving in my car
I arrived!
尾隨閉包語法在Swift中非常普遍,因此值得習(xí)慣仅孩。
當(dāng)閉包接受參數(shù)時(shí)托猩,將其用作參數(shù)
在這里,可以開始讀取閉包辽慕,有點(diǎn)像線噪聲:傳遞給函數(shù)的閉包也可以接受其自己的參數(shù)京腥。
我們用過的() -> Void
意思是“不接受任何參數(shù),什么也不返回”溅蛉,但是您可以繼續(xù)使用您的閉包()
應(yīng)該接受的任何參數(shù)的類型填充公浪。
為了說明這一點(diǎn),我們可以編寫一個(gè)travel()
函數(shù)船侧,該函數(shù)接受一個(gè)閉包作為唯一參數(shù)欠气,然后該閉包又接受一個(gè)字符串:
func travel(action: (String) -> Void) {
print("I'm getting ready to go.")
action("London")
print("I arrived!")
}
現(xiàn)在,當(dāng)我們使用尾隨閉包語法進(jìn)行調(diào)用travel()
時(shí)镜撩,需要我們的閉包代碼才能接受字符串:
travel { (place: String) in
print("I'm going to \(place) in my car")
}
此時(shí)预柒,place
的值為func travel(action: (String) -> Void)
中action
的值。輸出內(nèi)容如下:
I'm getting ready to go.
I'm going to London in my car
I arrived!
返回值時(shí)使用閉包作為參數(shù)
我們一直在用() -> Void
“不接受任何參數(shù),什么也不返回”的意思宜鸯,但是您可以將其替換Void
為任何類型的數(shù)據(jù)以強(qiáng)制閉包返回一個(gè)值憔古。
為了說明這一點(diǎn),我們可以編寫一個(gè)travel()
函數(shù)淋袖,該函數(shù)接受一個(gè)閉包作為其唯一參數(shù)鸿市,然后該閉包又接受一個(gè)字符串并返回一個(gè)字符串:
func travel(action: (String) -> String) {
print("I'm getting ready to go.")
let description = action("London")
print(description)
print("I arrived!")
}
現(xiàn)在,當(dāng)我們使用尾隨閉包語法進(jìn)行調(diào)用travel()
時(shí)即碗,需要我們的閉包代碼才能接受字符串并返回字符串:
travel { (place: String) -> String in
return "I'm going to \(place) in my car"
}
速記參數(shù)名稱
我們剛剛做了一個(gè)travel()
功能焰情。它接受一個(gè)參數(shù),這是一個(gè)閉包剥懒,其本身接受一個(gè)參數(shù)并返回一個(gè)字符串内舟。然后在兩次調(diào)用print()
之間運(yùn)行該閉包。
這就是代碼中的內(nèi)容:
func travel(action: (String) -> String) {
print("I'm getting ready to go.")
let description = action("London")
print(description)
print("I arrived!")
}
我們可以這樣調(diào)用travel()
:
travel { (place: String) -> String in
return "I'm going to \(place) in my car"
}
但是蕊肥,Swift 知道該閉包的參數(shù)必須是字符串谒获,因此我們可以將其刪除:
travel { place -> String in
return "I'm going to \(place) in my car"
}
它也知道閉包必須返回一個(gè)字符串,因此我們可以刪除它:
travel { place in
return "I'm going to \(place) in my car"
}
由于閉包只有一行代碼必須是返回值的那一行壁却,因此Swift也允許我們刪除return關(guān)鍵字:
travel { place in
"I'm going to \(place) in my car"
}
Swift具有簡化的語法批狱,可讓您走得更短。除了編寫place in
外展东,我們還可以讓Swift為閉包的參數(shù)提供自動(dòng)名稱赔硫。這些名稱以美元符號命名,然后從0開始計(jì)數(shù)盐肃。
travel {
"I'm going to \($0) in my car"
}
具有多個(gè)參數(shù)的閉包
為了確保一切都清楚爪膊,我們將使用兩個(gè)參數(shù)編寫另一個(gè)閉包示例。
這次砸王,我們的travel()
函數(shù)將需要一個(gè)閉合符推盛,該閉合符指定某人要去的地方以及他們要走的速度。這意味著我們需要使用(String, Int) -> String
參數(shù)的類型:
func travel(action: (String, Int) -> String) {
print("I'm getting ready to go.")
let description = action("London", 60)
print(description)
print("I arrived!")
}
我們將使用尾隨閉包和簡寫閉包參數(shù)名稱來調(diào)用它谦铃。由于此方法接受兩個(gè)參數(shù)耘成,因此我們將同時(shí)獲得$0
和$1
:
travel {
"I'm going to \($0) at \($1) miles per hour."
}
有些人不喜歡使用速記參數(shù)名稱,$0
因?yàn)檫@樣可能會造成混淆驹闰,這沒關(guān)系–盡一切可能為您效勞瘪菌。
從函數(shù)返回閉包
和你可以傳遞一個(gè)閉包給函數(shù)的方式相同,你也可以從函數(shù)獲取返回的閉包嘹朗。
第一次使用的語法有些混亂师妙,因?yàn)樗褂脙纱?code>->:一次用于指定函數(shù)的返回值,第二次用于指定閉包的返回值屹培。
為了解決這個(gè)問題默穴,我們將編寫一個(gè)不接受任何參數(shù)但返回閉包的函數(shù)travel()
怔檩。返回的閉包必須使用字符串調(diào)用,并且不會返回任何內(nèi)容壁顶。
這是Swift中的樣子:
func travel() -> (String) -> Void {
return {
print("I'm going to \($0)")
}
}
現(xiàn)在珠洗,我們可以調(diào)用travel()
以獲取該閉包,然后將其作為函數(shù)調(diào)用:
let result = travel()
result("London")
從技術(shù)上講是允許的-盡管實(shí)際上不建議這樣做若专!–travel()
直接從以下位置調(diào)用返回值:
let result2 = travel()("London")
值捕獲
如果您在閉包內(nèi)部使用任何外部值,則Swift會捕獲它們-將它們存儲在閉包旁邊蝴猪,因此即使它們不再存在调衰,也可以對其進(jìn)行修改。
現(xiàn)在自阱,我們有一個(gè)返回閉包的函數(shù)travel()
嚎莉,并且返回的閉包接受字符串作為其唯一參數(shù),并且不返回任何內(nèi)容:
func travel() -> (String) -> Void {
return {
print("I'm going to \($0)")
}
}
我們可以調(diào)用travel()
以獲取閉包沛豌,然后自由調(diào)用該閉包:
let result = travel()
result("London")
如果我們在閉包內(nèi)部使用travel()
要?jiǎng)?chuàng)建的值趋箩,則會發(fā)生閉包捕獲。例如加派,我們可能想跟蹤返回的閉包被調(diào)用的頻率:
func travel() -> (String) -> Void {
var counter = 1
return {
print("\(counter). I'm going to \($0)")
counter += 1
}
}
即使該counter
變量是在travel()
內(nèi)部創(chuàng)建的叫确,它也會被閉包捕獲,因此對于該閉包仍將保持活動(dòng)狀態(tài)芍锦。
因此竹勉,如果我們result("XXXX")
多次調(diào)用,計(jì)數(shù)器將不斷增加:
result("Beijing")
result("Wuhan")
result("Guangzhou")
此時(shí)輸出結(jié)果如下:
1. I'm going to Beijing
2. I'm going to Wuhan
3. I'm going to Guangzhou
總結(jié)
- 1.您可以為變量分配閉包娄琉,然后再調(diào)用它們次乓。
- 2.閉包可以接受參數(shù)和返回值,例如常規(guī)函數(shù)孽水。
- 3.您可以將閉包作為參數(shù)傳遞給函數(shù)票腰,并且這些閉包可以具有自己的參數(shù)和返回值。
- 4.如果函數(shù)的最后一個(gè)參數(shù)是閉包女气,則可以使用尾隨閉包語法杏慰。
- 5.Swift會自動(dòng)提供諸如
$0
和$1
的速記參數(shù)名稱,但并非所有人都使用它們主卫。 - 6.如果在閉包內(nèi)部使用外部值逃默,則將捕獲它們,以便閉包以后可以引用它們簇搅。