Optional Chaining as an Alternative to Forced Unwrapping
当你调用可选值的属性、方法或者下标的时候,我们可以在可选值后加
?来指定一个可选链。这个和放置!来强制解包是相似的。主要的不同
是可选链失败后会返回 nil,而后者将会触发运行时错误。
对于可选链来说 nil 也可以调用方法等,它永远返回可选值。你能检
查返回值来检验可选链是否调用成功。
下面的例子展示可选链和强制解包的不同
class Person {
var residence: Residence?
}
class Residence {
var numberOfRooms = 1
}
let john = Person()
//这将导致一个运行时异常
let roomCount = john.residence!.numberOfRooms
//下面的代码不会导致运行时的异常,注意返回值为可选值
if let roomCount = john.residence?.numberOfRooms {
print("John's residence has \(roomCount) room(s).")
} else {
print("Unable to retrieve the number of rooms.")
}
// Prints "Unable to retrieve the number of rooms."
john.residence = 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.")
}
// Prints "John's residence has 1 room(s)."
Defining Model Classes for Optional Chaining
我们先来定义接个类用于随后的列子。
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 buildingNumber != nil && street != nil {
return "\(buildingNumber) \(street)"
} else if buildingName != nil {
return buildingName
} 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.")
}
// Prints "Unable to retrieve the number of rooms."
因为 john.residence 是 nil,所以可选链的调用是失败的
我们可以通过可选链来建立属性值
let someAddress = Address()
someAddress.buildingNumber = "29"
someAddress.street = "Acacia Road"
john.residence?.address = someAddress
但是把 address 赋值给 john.residence 将要失败,因为 john.residence为 nil。
如果 john.residence? = Residence()呢?依然是失败的,因
为可选链发下 residence 没有值会直接返回 nil,这将导致等号右
边不会被执行(防止运行时异常吧)。
通过可选链调用方法
当通过可选链来调用一个方法时,我们可以通过检查返回值来查看调用
是否成功。即使方法没有返回值,我们也应该做这个检查。
Residence 的 printNumberOfRooms()方法
func printNumberOfRooms() {
print("The number of rooms is \(numberOfRooms)")
}
这个方法没有指定返回值,但是他仍然有个返回值类型 void,这意着
它会返回()值或者空元组
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.")
}
// Prints "It 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.")
}
// Prints "Unable to retrieve the first room name."
john.residence?[0] = Room(name: "Bathroom")
//这个建立新值的过程也是失败的,因为john.residence?返回nil
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.")
}
// Prints "The first room name is Living Room."
访问下标的可选类型
可选链的?要在可选值的后面。例如 Swift 的 Dictionary 类型
的返回值是可选的因此我们要把?放在返回值之后。
var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
testScores["Dave"]?[0] = 91
testScores["Bev"]?[0] += 1
testScores["Brian"]?[0] = 72
// the "Dave" array is now [91, 82, 84] and the "Bev" array is now [80, 94, 81]
链接多重水平的链
1.如果你尝试访问的值不是可选的,但是在多重可选链中返回值是可选的
2.如果你尝试取回的已经是可选的了,他将依然返回可选值,没有更高层的可选
因此如果你通过一个多重可选链访问一个 Int 值,那么返回值是int?,相似的,如果你取回一个 int?,int?是被返回,无论有多少水平的可选链。
if let johnsStreet = john.residence?.address?.street {
print("John's street name is \(johnsStreet).")
} else {
print("Unable to retrieve the address.")
}
// Prints "Unable to retrieve the address."
虽然 john.residence 是有值的,然john.residence.address
是 nil,所以当调用john.residence?.address?.street会失败
对方法的返回值放置可选链
我们可以用可选链调用一个方法,这个方法返回一个可选值,在这个可选值上添加可选链。
if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {
print("John's building identifier is \(buildingIdentifier).")
}
// Prints "John's building identifier is The Larches."
我们也可以在返回值中添加可选链
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\".")
}
}
// Prints "John's building identifier begins with "The"."