Swift:20 可選鏈
可選鏈是一種可以在當前值可能為nil的可選值上請求和調用屬性、方法及下標的方法。如果可選值有值,那么調用就會成功;如果可選值是nil,那么調用將返回nil。多個調用可以連接在一起形成一個調用鏈,如果其中任何一個節點為nil,整個調用鏈都會失敗,即返回nil。
使用可選鏈代替強制解包
通過在想調用的屬性、方法、或下標的可選值后面放一個問號(?),可以定義一個可選鏈。這一點很像在可選值后面放一個嘆號(!)來強制展開它的值。它們的主要區別在于當可選值為空時可選鏈式調用只會調用失敗,然而強制解包將會觸發運行時錯誤。
為了反映可選鏈式調用可以在空值(nil)上調用的事實,不論這個調用的屬性、方法及下標返回的值是不是可選值,它的返回結果都是一個可選值。你可以利用這個返回值來判斷你的可選鏈式調用是否調用成功,如果調用有返回值則說明調用成功,返回nil則說明調用失敗。
特別地,可選鏈式調用的返回結果與原本的返回結果具有相同的類型,但是被包裝成了一個可選值。例如,使用可選鏈式調用訪問屬性,當可選鏈式調用成功時,如果屬性原本的返回結果是Int類型,則會變為Int?類型。
下面演示代碼將展示可選鏈式調用和強制展開的不同。
class Player {
var name:String = "player name"
}
class Game {
var player : Player?
}
let game = Game()
//強制解包,將會引發運行時錯誤
//let name = game.player!.name
if let name = game.player?.name {
print("Player name : \(name).")
} else {
print("Unable get Player name.")
}
//打?。篣nable get Player name.
game.player = Player()
if let name = game.player?.name {
print("Player name : \(name).")
} else {
print("Unable get Player name.")
}
//打?。篜layer name : player name.
可選鏈應用在類上
通過使用可選鏈式調用可以調用多層屬性、方法和下標。這樣可以在復雜的類中向下訪問各種子屬性,并且判斷能否訪問子屬性的屬性、方法或下標。
下面這段代碼定義了四個模型類,演示了使用可選鏈訪問屬性、方法和下標。
class Person {
var residence: Residence?
}
class Residence {
var rooms = [Room]()
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
}
}
}
//訪問屬性
let john = Person()
if let roomCount = john.residence?.numberOfRooms {
print("John's residence has \(roomCount) room(s).")
} else {
print("Unable to retrieve the number of rooms.")
}
// 打?。篣nable to retrieve the number of rooms.
//設定屬性的值
let someAddress = Address()
someAddress.buildingNumber = "29"
someAddress.street = "Acacia Road"
john.residence?.address = someAddress //賦值不會被執行
//訪問方法
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.")
}
// 打?。篒t was not possible to print the number of rooms.
//訪問下標
if let firstRoomName = john.residence?[0].name {
print("The first room name is \(firstRoomName).")
} else {
print("Unable to retrieve the first room name.")
}
// 打?。篣nable to retrieve the first room name.
//下標賦值
john.residence?[0] = Room(name: "Bathroom")
//下標和屬性組合使用
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.")
}
// 打?。篢he first room name is Living Room.
連接多層可選鏈式調用
可以通過連接多個可選鏈式調用在更深的模型層級中訪問屬性、方法以及下標。然而,多層可選鏈式調用不會增加返回值的可選層級。
也就是說:
- 如果你訪問的值不是可選的,可選鏈式調用將會返回可選值。
- 如果你訪問的值就是可選的,可選鏈式調用不會讓可選返回值變得“更可選”。
因此:
- 通過可選鏈式調用訪問一個Int值,將會返回Int?,無論使用了多少層可選鏈式調用。
- 類似的,通過可選鏈式調用訪問Int?值,依舊會返回Int?值,并不會返回Int??。
下面的例子嘗試訪問john中的residence屬性中的address屬性中的street屬性。這里使用了兩層可選鏈式調用,residence以及address都是可選值:
//john.residence?.address?.street的返回值也依然是String?,
//即使已經使用了兩層可選鏈式調用。
if let johnsStreet = john.residence?.address?.street {
print("John's street name is \(johnsStreet).")
} else {
print("Unable to retrieve the address.")
}
// 打印 “Unable to retrieve the address.”
在方法的可選返回值上進行可選鏈式調用
上面的例子展示了如何在一個可選值上通過可選鏈式調用來獲取它的屬性值。我們還可以在一個可選值上通過可選鏈式調用來調用方法,并且可以根據需要繼續在方法的可選返回值上進行可選鏈式調用。
在下面的例子中,通過可選鏈式調用來調用Address的buildingIdentifier()方法。這個方法返回String?類型的值。如果要在該方法的返回值上進行可選鏈式調用,在方法的圓括號后面加上問號即可:
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\".")
}
}
// 打?。篔ohn's building identifier begins with "The".