可選鏈?zhǔn)秸{(diào)用(Optional Chaining)是一種可以在當(dāng)前值可能為nil
的可選值上請求和調(diào)用屬性、方法及下標(biāo)的方法。如果可選值有值违寿,那么調(diào)用就會(huì)成功湃交;如果可選值是nil
,那么調(diào)用將返回nil
藤巢。多個(gè)調(diào)用可以連接在一起形成一個(gè)調(diào)用鏈搞莺,如果其中任何一個(gè)節(jié)點(diǎn)為nil
,整個(gè)調(diào)用鏈都會(huì)失敗掂咒,即返回nil
才沧。
使用可選鏈?zhǔn)秸{(diào)用代替強(qiáng)制展開
通過在想調(diào)用的屬性、方法绍刮、或下標(biāo)的可選值(optional value)后面放一個(gè)問號(hào)(?
)温圆,可以定義一個(gè)可選鏈。這一點(diǎn)很像在可選值后面放一個(gè)嘆號(hào)(!
)來強(qiáng)制展開它的值孩革。它們的主要區(qū)別在于當(dāng)可選值為空時(shí)可選鏈?zhǔn)秸{(diào)用只會(huì)調(diào)用失敗岁歉,然而強(qiáng)制展開將會(huì)觸發(fā)運(yùn)行時(shí)錯(cuò)誤。
為了反映可選鏈?zhǔn)秸{(diào)用可以在空值(nil
)上調(diào)用的事實(shí)膝蜈,不論這個(gè)調(diào)用的屬性锅移、方法及下標(biāo)返回的值是不是可選值,它的返回結(jié)果都是一個(gè)可選值饱搏。你可以利用這個(gè)返回值來判斷你的可選鏈?zhǔn)秸{(diào)用是否調(diào)用成功非剃,如果調(diào)用有返回值則說明調(diào)用成功,返回nil
則說明調(diào)用失敗推沸。
通過可選鏈?zhǔn)秸{(diào)用訪問屬性
let someAddress = Address()
someAddress.buildingNumber = "29"
someAddress.street = "Acacia Road"
john.residence?.address = someAddress
在這個(gè)例子中努潘,通過john.residence
來設(shè)定address
屬性也會(huì)失敗,因?yàn)?code>john.residence當(dāng)前為nil
坤学。
上面代碼中的賦值過程是可選鏈?zhǔn)秸{(diào)用的一部分疯坤,這意味著可選鏈?zhǔn)秸{(diào)用失敗時(shí),等號(hào)右側(cè)的代碼不會(huì)被執(zhí)行深浮。對于上面的代碼來說压怠,很難驗(yàn)證這一點(diǎn),因?yàn)橄襁@樣賦值一個(gè)常量沒有任何副作用飞苇。下面的代碼完成了同樣的事情菌瘫,但是它使用一個(gè)函數(shù)來創(chuàng)建Address
實(shí)例,然后將該實(shí)例返回用于賦值布卡。該函數(shù)會(huì)在返回前打印“Function was called”雨让,這使你能驗(yàn)證等號(hào)右側(cè)的代碼是否被執(zhí)行。
func createAddress() -> Address {
print("Function was called.")
let someAddress = Address()
someAddress.buildingNumber = "29"
someAddress.street = "Acacia Road"
return someAddress
}
john.residence?.address = createAddress()
沒有任何打印消息忿等,可以看出createAddress()
函數(shù)并未被執(zhí)行栖忠。
在可選鏈?zhǔn)秸{(diào)用的過程中,一旦失敗,后面的代碼都不會(huì)被執(zhí)行
通過可選鏈?zhǔn)秸{(diào)用調(diào)用方法
沒有返回值的方法具有隱式的返回類型Void
,如果在可選值上通過可選鏈?zhǔn)秸{(diào)用來調(diào)用這個(gè)方法庵寞,該方法的返回類型會(huì)是Void?
狸相,而不是Void
,因?yàn)橥ㄟ^可選鏈?zhǔn)秸{(diào)用得到的返回值都是可選的捐川。這樣我們就可以使用if
語句來判斷能否成功調(diào)用方法脓鹃。即使方法本身沒有定義返回值。
if john.residence?.printNumberOfRooms() != nil {
print("It was possible to print the number of rooms.")
} else {
print("It was not possible to print the number of rooms.")
}
// 打印 “It was not possible to print the number of rooms.”
通過可選鏈?zhǔn)秸{(diào)用訪問下標(biāo)
ps: 通過可選鏈?zhǔn)秸{(diào)用訪問可選值的下標(biāo)時(shí)古沥,應(yīng)該將問號(hào)放在下標(biāo)方括號(hào)的前面而不是后面瘸右。可選鏈?zhǔn)秸{(diào)用的問號(hào)一般直接跟在可選表達(dá)式的后面岩齿。
if let firstRoomName = john.residence?[0].name {
print("The first room name is \(firstRoomName).")
} else {
print("Unable to retrieve the first room name.")
}
// 打印 “Unable to retrieve the first room name.”
類似的尊浓,可以通過下標(biāo),用可選鏈?zhǔn)秸{(diào)用來賦值:
john.residence?[0] = Room(name: "Bathroom")
這次賦值同樣會(huì)失敗纯衍,因?yàn)?code>residence目前是nil
栋齿。
訪問可選類型的下標(biāo)
如果下標(biāo)返回可選類型值,比如 Swift 中Dictionary
類型的鍵的下標(biāo)襟诸,可以在下標(biāo)的結(jié)尾括號(hào)后面放一個(gè)問號(hào)來在其可選返回值上進(jìn)行可選鏈?zhǔn)秸{(diào)用:
var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
testScores["Dave"]?[0] = 91
testScores["Bev"]?[0]++
testScores["Brian"]?[0] = 72
// "Dave" 數(shù)組現(xiàn)在是 [91, 82, 84]瓦堵,"Bev" 數(shù)組現(xiàn)在是 [80, 94, 81]
連接多層可選鏈?zhǔn)秸{(diào)用
可以通過連接多個(gè)可選鏈?zhǔn)秸{(diào)用在更深的模型層級(jí)中訪問屬性、方法以及下標(biāo)歌亲。然而菇用,多層可選鏈?zhǔn)秸{(diào)用不會(huì)增加返回值的可選層級(jí)。
也就是說:
- 如果你訪問的值不是可選的陷揪,可選鏈?zhǔn)秸{(diào)用將會(huì)返回可選值惋鸥。
- 如果你訪問的值就是可選的,可選鏈?zhǔn)秸{(diào)用不會(huì)讓可選返回值變得“更可選”悍缠。
因此:
- 通過可選鏈?zhǔn)秸{(diào)用訪問一個(gè)
Int
值卦绣,將會(huì)返回Int?
,無論使用了多少層可選鏈?zhǔn)秸{(diào)用飞蚓。 - 類似的滤港,通過可選鏈?zhǔn)秸{(diào)用訪問
Int?
值,依舊會(huì)返回Int?
值趴拧,并不會(huì)返回Int??
溅漾。
要點(diǎn)總結(jié)
可選鏈?zhǔn)秸{(diào)用的過程中,一旦失敗則后續(xù)的代碼都不會(huì)執(zhí)行著榴。也就是說添履,在這種情況下,去調(diào)用一個(gè)nil的對方的方法是不會(huì)崩潰的脑又。
另外一點(diǎn)暮胧。返回值為void的函數(shù)锐借,返回的是()?。