可選鏈式調(diào)用是一種可以在當前值可能為 nil 的可選值上請求和調(diào)用屬性、方法及下標的方法礼旅。
如果可選值有值,那么調(diào)用就會成功;如果可選值是 nil ,那么調(diào)用將返回 nil 。
多個調(diào)用可以連接在一起形成一個調(diào)用鏈公给,如果其中任何一個節(jié)點為 nil ,整個調(diào)用鏈都會失敗蜘渣,即返回 nil 淌铐。
1、使用可選鏈式調(diào)用代替強制展開
下面幾段代碼將解釋可選鏈式調(diào)用和強制展開的不同:
首先定義兩個類 Person 和 Residence :
class Person {
var residence: Residence?
}
class Residence {
var numberOfRooms = 1
}
let john = Person()
如果使用嘆號( ! )強制展開獲得這個 john 的 residence 屬性中的 numberOfRooms 值蔫缸,會觸發(fā)運行時錯誤腿准,因為這時 residence 沒有可以展開的值:
let roomCount = john.residence!.numberOfRooms // 這會引發(fā)運行時錯誤
john.residence 為非 nil 值的時候,上面的調(diào)用會成功捂龄,并且把 roomCount 設(shè)置為 Int 類型的房間數(shù)量释涛。正如上 面提到的,當 residence 為 nil 的時候上面這段代碼會觸發(fā)運行時錯誤倦沧。
//可選鏈式調(diào)用提供了另一種訪問 numberOfRooms 的方式唇撬,使用問號( ? )來替代原來的嘆號( ! ):
if let roomCount = john.residence?.numberOfRooms {
print("John's residence has \(roomCount) room(s).")
} else {
print("Unable to retrieve the number of rooms.")
}
// 打印 “Unable to retrieve the number of rooms.”
要注意的是,即使 numberOfRooms 是非可選的 Int 時展融,這一點也成立窖认。只要使用可選鏈式調(diào)用就意味著numberOfRooms 會返回一個 Int? 而不是 Int 。
// 可以將一個 Residence 的實例賦給 john.residence ,這樣它就不再是 nil 了:
john.residence = Residence()
if let roomCount = john.residence?.numberOfRooms {
print("John's residence has \(roomCount) room(s).")
} else {
print("Unable to retrieve the number of rooms.")
}
// 打印 “John's residence has 1 room(s).”
*/
2扑浸、為可選鏈式調(diào)用定義模型類
通過使用可選鏈式調(diào)用可以調(diào)用多層屬性烧给、方法和下標。這樣可以在復(fù)雜的模型中向下訪問各種子屬性喝噪,并且判斷能否訪問子屬性的屬性础嫡、方法或下標。
eg:
class Person {
var residence: Residence?
}
class Residence {
var rooms = [Room]()//增加了一個名為 rooms 的變量屬性酝惧,該屬性被初始化為 [Room] 類型的空數(shù)組:
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
get {
return rooms[i]
}
set {
rooms[i] = newValue
}
}
func printNumberOfRooms() {
print("The number of rooms is \(numberOfRooms)")
}
var address: Address?
}
class Room {
let name: String
init(name: String) { self.name = name }
}
class Address {
var buildingName: String?
var buildingNumber: String?
var street: String?
func buildingIdentifier() -> String? {
if buildingName != nil {
return buildingName
} else if buildingNumber != nil && street != nil {
return "\(buildingNumber) \(street)"
} else {
return nil
}
}
}
3榴鼎、通過可選鏈式調(diào)用訪問屬性
可以通過可選鏈式調(diào)用在一個可選值上訪問它的屬性,并判斷訪問是否成功晚唇。
下面的代碼創(chuàng)建了一個 Person 實例巫财,然后像之前一樣,嘗試訪問 numberOfRooms 屬性:
let john = Person()
//因為 john.residence 為 nil 哩陕,所以這個可選鏈式調(diào)用依舊會像先前一樣失敗平项。
if let roomCount = john.residence?.numberOfRooms {
print("John's residence has \(roomCount) room(s).")
} else {
print("Unable to retrieve the number of rooms.")
}
// 打印 “Unable to retrieve the number of rooms.”
還可以通過可選鏈式調(diào)用來設(shè)置屬性值:
eg:
let someAddress = Address()
someAddress.buildingNumber = "29"
someAddress.street = "Acacia Road"
john.residence?.address = someAddress
print("address:\(john.residence?.address)")//nil
//在這個例子中,通過 john.residence 來設(shè)定 address 屬性也會失敗悍及,因為 john.residence 當前為 nil 闽瓢。
下面的代碼完成了同樣的事情,但是它使用一個函數(shù)來創(chuàng)建 Address 實例并鸵,然后將該實例返回用于賦值鸳粉。
該函數(shù)會在返回前打印“Funct ion was called”,這使你能驗證等號右側(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()//驗證等號右側(cè)的代碼是否被執(zhí)行届谈。
//沒有任何打印消息,可以看出 createAddress() 函數(shù)并未被執(zhí)行弯汰。
4艰山、通過可選鏈式調(diào)用調(diào)用方法
//可以通過可選鏈式調(diào)用來調(diào)用方法,并判斷是否調(diào)用成功咏闪,即使這個方法沒有返回值曙搬。
//通過判斷返回值是否為 nil 可以判斷調(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.”
//同樣的,可以據(jù)此判斷通過可選鏈式調(diào)用為屬性賦值是否成功
if (john.residence?.address = someAddress) != nil {
print("It was possible to set the address.")
} else {
print("It was not possible to set the address.")
}
// 打印 “It was not possible to set the address.”
5鸽嫂、通過可選鏈式調(diào)用訪問下標
通過可選鏈式調(diào)用纵装,我們可以在一個可選值上訪問下標,并且判斷下標調(diào)用是否成功据某。
注意:
通過可選鏈式調(diào)用訪問可選值的下標時橡娄,應(yīng)該將問號放在下標方括號的前面而不是后面⊙⒆眩可選鏈式調(diào)用的問號一 般直接跟在可選表達式的后面挽唉。
eg:
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.”
//類似的滤祖,可以通過下標,用可選鏈式調(diào)用來賦值:
john.residence?[0] = Room(name: "Bathroom")//這次賦值同樣會失敗瓶籽,因為residence目前是nil 匠童。
如果你創(chuàng)建一個 Residence 實例,并為其 rooms 數(shù)組添加一些 Room 實例塑顺,然后將 Residence 實例賦值給john.residence 汤求,那就可以通過可選鏈和下標來訪問數(shù)組中的元素:
eg:
let johnsHouse = Residence()
johnsHouse.rooms.append(Room(name: "Living Room"))
johnsHouse.rooms.append(Room(name: "Kitchen"))
john.residence = johnsHouse
if let firstRoomName = john.residence?[0].name {
print("The first room name is \(firstRoomName).")
} else {
print("Unable to retrieve the first room name.")
}
// 打印 “The first room name is Living Room.”
//訪問可選類型的下標
var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
testScores["Dave"]?[0] = 91
testScores["Bev"]?[0] += 1
testScores["Brian"]?[0] = 72//調(diào)用失敗
// "Dave" 數(shù)組現(xiàn)在是 [91, 82, 84],"Bev" 數(shù)組現(xiàn)在是 [80, 94, 81]
6严拒、連接多層可選鏈式調(diào)用
? 通過可選鏈式調(diào)用訪問一個 Int 值首昔,將會返回 Int? ,無論使用了多少層可選鏈式調(diào)用糙俗。
? 類似的,通過可選鏈式調(diào)用訪問 Int? 值预鬓,依舊會返回 Int? 值巧骚,并不會返回 Int?? 。
let johnsAddress = Address()
johnsAddress.buildingName = "The Larches"
johnsAddress.street = "Laurel Street"
john.residence?.address = johnsAddress
if let johnsStreet = john.residence?.address?.street {//多層可選
print("John's street name is \(johnsStreet).")
} else {
print("Unable to retrieve the address.")
}
// 打印 “John's street name is Laurel Street.”
7格二、在方法的可選返回值上進行可選鏈式調(diào)用
在下面的例子中劈彪,通過可選鏈式調(diào)用來調(diào)用 Address 的 buildingIdentifier() 方法。這個方法返回 String? 類型 的值顶猜。如上所述沧奴,通過可選鏈式調(diào)用來調(diào)用該方法,最終的返回值依舊會是 String? 類型:
if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {
print("John's building identifier is \(buildingIdentifier).")
}
// 打印 “John's building identifier is The Larches.”
//如果要在該方法的返回值上進行可選鏈式調(diào)用长窄,在方法的圓括號后面加上問號即可:
if let beginsWithThe = john.residence?.address?.buildingIdentifier()?.hasPrefix("The") {
if beginsWithThe {
print("John's building identifier begins with \"The\".")
} else {
print("John's building identifier does not begin with \"The\".")
} }
// 打印 “John's building identifier begins with "The".”
注意:
在上面的例子中滔吠,在方法的圓括號后面加上問號是因為你要在 buildingIdentifier() 方法的可選返回值上進行可選鏈式調(diào)用,而不是方法本身挠日。