可选链式调用

可选链式调用是在当前值可能为nil的可选值上请求和调用属性、方法以及下标的方法。如果可选值不为空,调用成功,否则如果可选值为nil,那么调用将返回nil。多个调用可以结合为一个调用链,若任何一个节点为nil。整个调用链都会失败

可选链式调用代替强制展开

在想调用的属性、方法、下标的可选值后加上?来定义一个可选链。

很像在可选值后面放一个叹号(!)来强制展开它的值。它们的主要区别在于当可选值为空时可选链式调用只会调用失败,然而强制展开将会触发运行时错误

可选链式调用,不论调用的属性、方法及下标返回值的类型,返回结果都是具有相同类型的可选值,可以利用这个可选返回值来判断链式调用是否成功,有值则返回成功,nil表示调用失败

class Person {
    var residence: Residence?
}
class Residence {
    var numberOfRooms = 1
}
let john = Person()

我们直接let roomCount = john.residence!.numberOfRooms因为residencenil强制展开的话是会触发运行时错误的

这时我们可以采用可选链式调用来访问numberOfRooms?代替!

if let roomCount = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}

添加问号之后,Swift 就会在residence不为nil的情况下访问numberOfRooms

即使numberOfRooms是非可选类型Int,只要使用可选链式调用就意味着numberOfRooms会返回Int?而不是Int

为可选链式调用定义模型类

通过可选链式调用访问属性

通过可选链式调用访问可能不存在实例的属性:

//john.residence实例可能为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.")
}

通过可选链式调用设置属性值

//john.residence实例可能为nil
let someAddress = Address()
john.residence?.address = someAddress

注意:

可以通过可选属性值 设置属性值

对于这种可选链式调用的赋值,如果john.residencenil,获取address属性失败,那么右侧的代码是不会被执行的

通过可选链式调用调用方法

对于Residence类中定义的方法

func printNumberOfRooms() {
    print("The number of rooms is \(numberOfRooms)")
}

这个方法没有返回值,那就是具有隐式的返回值Void
那么如果用可选链式调用这个方法,返回值就为Void?

//john.residence 为Residence类 可能为nil的实例
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.")
}
//可以根据返回值是否为nil 判断调用是否成功

对实例中的属性赋值 也会返回一个可选类型

if (john.residence?.address = someAddress) != nil {
print("It was possible to set the address.")
} else {
print("It was not possible to set the address.")
}

注意

没有返回值的函数可选链式调用后返回的时 void?

通过可选链式调用访问下标

可以在一个可选值上访问下标,判断下标是否成功

//john.residence可能为nil
//问号放在下标前面表示john.residence是可选值
if let firstRoomName = john.residence?[0].name {
    print("The first room name is \(firstRoomName).")
} else {
    print("Unable to retrieve the first room name.")
}

注意:

可选链式调用访问可选值下标时,将问号放在下标方括号的前面而不是后面

john.residence?[0] = Room(name: "Bathroom")

john.residence为nil时,赋值仍然会失败

访问可选类型的下标

如果下标返回可选类型值,可以在下标的结尾括号后面放一个问号来在其可选返回值上进行可选链式调用

var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
testScores["Dave"]?[0] = 91
testScores["Bev"]?[0] += 1
testScores["Brian"]?[0] = 72

定义一个字典,包含两个键值对,因为字典下标返回的是可选类型值,对于testScores["Brian"]因为字典中没有这个键,所以返回为nil,调用失败

注意:

如果下表返回可选类型值,可以在下标结尾括号后放?

连接多层可选链式调用

可以连接多个可选链式调用在更深模型层级中访问属性、方法、下标,但是是不会增加返回值的可选层级的

  • 通过可选链式调用访问一个 Int 值,将会返回 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.")
}

john.residence.addressnil因此调用失败
尽管street属性为String? 返回值仍然为String?

在方法的可选返回值上进行可选链式调用

if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {
    print("John's building identifier is \(buildingIdentifier).")
}

在可选值的基础上调用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\".")
    }
}

在方法的返回值基础上进行可选链式调用

在方法的圆括号后面加上问号即可