可选链 (Optional Chainning)
可选链是一种可以请求和调用属性、方法和子脚本的过程,用于请求或调用的目标可能为nil,如果目标有值,调用就会成功,如果目标为nil,调用将返回nil,多次请求或调用可以被链接成一个链,如果任意一个节点为nil将导致整条链失效
为了反映可选链可以调用nil,无论调用的方法、属性或子脚本返回的值是不是可选值,他的返回结果都是一个可选值,利用这个返回值检验可选链调用是否成功,返回nil则失败
调用可选链的返回结果与原本的返回结果具有相同的类型,但原本的返回结果被包装成了一个可选值,当可选链调用成功时,一个应该返回Int的属性将返回Int?
可选链是一种可以请求和调用属性、方法和子脚本的过程,用于请求或调用的目标可能为nil,如果目标有值,调用就会成功,如果目标为nil,调用将返回nil,多次请求或调用可以被链接成一个链,如果任意一个节点为nil将导致整条链失效
1.可选链替代强制解析
在向调用的属性、方法或子脚本的可选值后面加一个问号,可以定义一个可选链,就像在可选值后面放一个生命符号来强制获得封包内的值,区别在于可选值为空时可选链马上失败,而强制解析将会引发运行时错误为了反映可选链可以调用nil,无论调用的方法、属性或子脚本返回的值是不是可选值,他的返回结果都是一个可选值,利用这个返回值检验可选链调用是否成功,返回nil则失败
调用可选链的返回结果与原本的返回结果具有相同的类型,但原本的返回结果被包装成了一个可选值,当可选链调用成功时,一个应该返回Int的属性将返回Int?
class Person {
var residence: Residence?
}
class Residence {
var numberOfRooms = 1
}
// 创建一个新的Person实例时,他的residence属性由于被定义为自判断类型的,此属性将默认初始化为空
let john = Person()
// 此时如果想用 ! 来强制解析获得residence的numberOfRooms的属性值,将会引发运行时错误,因为此时没有可供解析的residence值
// 当john.residence 不为nil,会运行通过,还会将roomCount赋值,然而当residence为空时将引发运行时错误
let roomCount = john.residence!.numberOfRooms
// this triggers a runtime error
// 以下是用可选链来获得numberOfRooms 的方法,使用 ? 来替代 ! 的位置,Swift自判断链接 residence 属性,如果residence存在则取 numberOfRooms 的值,如果为nil,将无法访问numberOfRooms
if let roomCount = john.residence?.numberOfRooms {
println("John's residence has \(roomCount) room(s).")
} else {
println("Unable to retrieve the number of rooms.")
}
// prints "Unable to retrieve the number of rooms."
// 自定义一个Residence实例给 john.residence
john.residence = Residence()
if let roomCount = john.residence?.numberOfRooms {
println("John's residence has \(roomCount) room(s).")
} else {
println("Unable to retrieve the number of rooms.")
}
// prints "John's residence has 1 room(s)."
2.为可选链定义模型类
用可选链来多层调用属性、方法和子脚本,利用他们之间的复杂模型来获取更底层的属性,并检查是否可以成功获取此类底层属性class Person {
var residence: Residence?
}
class Residence {
var rooms = Room[]()
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
return rooms[i]
}
func printNumberOfRooms() {
println("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 {
return buildingName
} else if buildingNumber {
return buildingNumber
} else {
return nil
}
}
}
3.通过可选链调用属性
// 使用上述定义的类新建一个实例,尝试访问 numberOfRooms 属性,由于john.residence 为空,所以这个可选链和之前一样失败,但没有运行时错误
let john = Person()
if let roomCount = john.residence?.numberOfRooms {
println("John's residence has \(roomCount) room(s).")
} else {
println("Unable to retrieve the number of rooms.")
}
// prints "Unable to retrieve the number of rooms."
4.通过可选链调用方法
使用可选链来调用可选值的方法并检查方法调用是否成功,即使这个方法没有返回值,依然可以使用可选链来达到这个目的func printNumberOfRooms() {
println("The number of rooms is \(numberOfRooms)")
}
当用可选链调用上一个方法时,方法返回值类型是void? 而不是 void,因为通过可选链调用方法时返回值总是可选类型的,即使这个方法本来没有定义返回值,也可以用if语句检查是否成功调用printNumberOfRooms方法
if john.residence?.printNumberOfRooms() {
println("It was possible to print the number of rooms.")
} else {
println("It was not possible to print the number of rooms.")
}
// prints "It was not possible to print the number of rooms."
5.使用可选链调用子脚本
可以使用可选链来尝试从子脚本中获取值并检查子脚本的调用是否成功,但不能通过可选链来设置子代码// 在Residence类中定义的子脚本获取 john.residence 数组中第一个房间的名字,因为 john.residence 现在是nil,调用失败
if let firstRoomName = john.residence?[0].name {
println("The first room name is \(firstRoomName).")
} else {
println("Unable to retrieve the first room name.")
}
// prints "Unable to retrieve the first room name."
let johnsHouse = Residence()
johnsHouse.rooms += Room(name: "Living Room")
johnsHouse.rooms += Room(name: "Kitchen")
john.residence = johnsHouse
if let firstRoomName = john.residence?[0].name {
println("The first room name is \(firstRoomName).")
} else {
println("Unable to retrieve the first room name.")
}
// prints "The first room name is Living Room."
6.连接多层链接
if let johnsStreet = john.residence?.address?.street {
println("John's street name is \(johnsStreet).")
} else {
println("Unable to retrieve the address.")
}
// prints "Unable to retrieve the address."
let johnsAddress = Address()
johnsAddress.buildingName = "The Larches"
johnsAddress.street = "Laurel Street"
john.residence!.address = johnsAddress
if let johnsStreet = john.residence?.address?.street {
println("John's street name is \(johnsStreet).")
} else {
println("Unable to retrieve the address.")
}
// prints "John's street name is Laurel Street."
7.链接自判断返回值的方法
if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {
println("John's building identifier is \(buildingIdentifier).")
}
// prints "John's building identifier is The Larches."
if let upper = john.residence?.address?.buildingIdentifier()?.uppercaseString {
println("John's uppercase building identifier is \(upper).")
}
// prints "John's uppercase building identifier is THE LARCHES."