★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ ➤微信公众号:山青咏芝(shanqingyongzhi) ➤博客园地址:山青咏芝(https://www.cnblogs.com/strengthen/) ➤GitHub地址:https://github.com/strengthen/LeetCode ➤原文地址:https://www.cnblogs.com/strengthen/p/9739421.html ➤如果链接不是山青咏芝的博客园地址,则可能是爬取作者的文章。 ➤原文已修改更新!强烈建议点击原文地址阅读!支持作者!支持原创! ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
热烈欢迎,请直接点击!!!
进入博主App Store主页,下载使用各个作品!!!
注:博主将坚持每月上线一个新app!!!
可选链接是用于查询和调用当前可能是的可选属性,方法和下标的过程nil 。如果可选包含值,则属性,方法或下标调用成功;否则,调用成功。如果可选参数为nil ,则属性,方法或下标调用返回nil 。可以将多个查询链接在一起,如果链中的任何链接为,则整个链都会正常失败nil 。
笔记
Swift中的可选链接类似于nil Objective-C中的消息传递,但是它适用于任何类型,并且可以检查成功或失败。
可选链接作为强制展开的替代方法
您可以通过在? 要调用其属性,方法或下标的可选值之后放置问号()来指定可选链,如果可选值不是- nil 。这非常类似于将感叹号(! )放在可选值之后以强制展开其值。主要区别在于,当可选选项为时,可选链接会优雅地失败nil ,而当可选选项为时,强制展开会触发运行时错误nil 。
为了反映可以在nil 值上调用可选链接的事实,即使您要查询的属性,方法或下标返回非可选值,可选链接调用的结果也始终是可选值。您可以使用此可选返回值来检查可选链接调用是否成功(返回的可选值包含一个值),或者由于nil 链中的值而失败(返回的可选值是nil )。
具体来说,可选链接调用的结果与期望的返回值具有相同的类型,但包装在可选中。通过可选链接访问时,通常返回an的属性Int 将返回a Int? 。
接下来的几个代码片段演示了可选链接与强制展开如何不同,并使您能够检查是否成功。
首先,定义了两个类Person ,Residence 分别定义为和:
- class Person {
- var residence: Residence?
- }
-
- class Residence {
- var numberOfRooms = 1
- }
Residence 实例有一个Int 名为的属性numberOfRooms ,默认值为1 。Person 实例具有residence 类型的可选属性Residence? 。
如果创建一个新Person 实例,则其residence 属性默认为nil ,因为它是可选的。在下面的代码中,john 其residence 属性值为nil :
如果您尝试访问numberOfRooms 此人的属性residence ,则通过在其后residence 强制放置一个感叹号来对其值进行解包,则会触发运行时错误,因为没有residence 要解包的值:
- let roomCount = john.residence!.numberOfRooms
- // this triggers a runtime error
上面的代码在john.residence 为非nil 值时成功,并将设置roomCount 为Int 包含适当数量房间的值。但是,如上所述,此代码始终会在residence is时触发运行时错误nil 。
可选链接为访问的值提供了另一种方法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."
这告诉Swift将“链”到可选residence 属性上,并检索numberOfRooms ifresidence 存在的值。
由于尝试访问numberOfRooms 有可能失败,因此可选的链接尝试将返回typeInt? 或“ optional Int ”的值。如上例所示,当residence 为is时nil ,此可选参数Int 也将为nil ,以反映无法访问的事实numberOfRooms 。可选的Int 是通过可选的结合解开的整数并分配非可选值到访问roomCount 恒定。
请注意,即使这numberOfRooms 是非可选的,也是如此Int 。通过可选链查询它的事实意味着,对的调用numberOfRooms 将始终返回Int? 而不是Int 。
您可以将Residence 实例分配给john.residence ,以使其不再具有nil 值:
- john.residence = Residence()
john.residence 现在包含一个实际Residence 实例,而不是nil 。如果您尝试使用numberOfRooms 与以前相同的可选链接进行访问,则它将返回一个Int? 包含默认numberOfRooms 值的1 :
- 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)."
定义可选链接的模型类
您可以将可选链接与深度超过一级的属性,方法和下标一起使用。这使您可以深入研究相互关联类型的复杂模型中的子属性,并检查是否可以访问这些子属性上的属性,方法和下标。
下面的代码段定义了四个模型类,以供随后的几个示例中使用,包括多级可选链接的示例。这些类通过添加和类以及相关的属性,方法和下标,从上面扩展了Person andResidence 模型。Room Address
该Person 班以同样的方式前的定义:
- class Person {
- var residence: Residence?
- }
该Residence 班是比以前更加复杂。这次,Residence 该类定义了一个名为的变量属性rooms ,该属性用一个类型为空的数组初始化[Room] :
- 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?
- }
因为此版本的Residence 存储Room 实例数组,所以其numberOfRooms 属性实现为计算属性,而不是存储属性。计算的numberOfRooms 属性只是count 从rooms 数组中返回该属性的值。
作为访问其rooms 数组的捷径,此版本Residence 提供了一个读写下标,该下标提供对rooms 数组中请求的索引处的房间的访问。
此版本的版本Residence 还提供了一种称为的方法printNumberOfRooms ,该方法可以简单地打印住宅中的房间数量。
最后,Residence 定义一个名为的可选属性address ,类型为Address? 。Address 此属性的类类型在下面定义。
在Room 用于类rooms 阵列是简单的类与一种属性调用name ,以及一个初始值设定到该属性设置为一个合适的房间名称:
- class Room {
- let name: String
- init(name: String) { self.name = name }
- }
此模型中的最后一个类称为Address 。此类具有type的三个可选属性String? 。前两个属性buildingName 和buildingNumber 是标识特定建筑物作为地址一部分的替代方法。第三个属性,street 用于为该地址命名街道:
- class Address {
- var buildingName: String?
- var buildingNumber: String?
- var street: String?
- func buildingIdentifier() -> String? {
- if let buildingNumber = buildingNumber, let street = street {
- return "\(buildingNumber) \(street)"
- } else if buildingName != nil {
- return buildingName
- } else {
- return nil
- }
- }
- }
所述Address 类还提供了一个名为方法buildingIdentifier() ,其具有的返回类型String? 。此方法检查所述地址的属性,并返回buildingName ,如果它有一个值,或buildingNumber 与级联street 如果两者都具有值,或nil 以其他方式。
通过可选链接访问属性
如可选链作为强制解包的替代中所示,您可以使用可选链来访问可选值上的属性,并检查该属性访问是否成功。
使用上面定义的类创建一个新Person 实例,并尝试numberOfRooms 像以前一样访问其属性:
- 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 。
分配是可选链接的一部分,这意味着不会= 评估运算符右侧的任何代码。在前面的示例中,很难看到它someAddress 从未被评估过,因为访问常量没有任何副作用。下面的清单执行相同的分配,但是它使用一个函数来创建地址。该函数在返回值之前打印“调用了函数”,这使您可以查看是否对= 运算符的右侧进行了评估。
- func createAddress() -> Address {
- print("Function was called.")
-
- let someAddress = Address()
- someAddress.buildingNumber = "29"
- someAddress.street = "Acacia Road"
-
- return someAddress
- }
- john.residence?.address = createAddress()
您可以说createAddress() 没有调用该函数,因为没有打印任何内容。
通过可选链接调用方法
您可以使用可选链接来对可选值调用方法,并检查该方法调用是否成功。即使该方法未定义返回值,也可以执行此操作。
在printNumberOfRooms() 对方法Residence 类打印的当前值numberOfRooms 。该方法的外观如下:
- func printNumberOfRooms() {
- print("The number of rooms is \(numberOfRooms)")
- }
此方法未指定返回类型。但是,没有返回类型的函数和方法具有隐式返回类型Void ,如无返回值的函数中所述。这意味着它们返回的值为() ,或者为空的元组。
如果通过可选链对可选值调用此方法,则该方法的返回类型将为Void? ,而不是Void ,因为通过可选链调用时,返回值始终为可选类型。这使您可以使用if 语句检查是否可以调用该printNumberOfRooms() 方法,即使该方法本身未定义返回值。将printNumberOfRooms 调用的返回值与之进行比较,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.")
- }
- // Prints "It was not possible to print the number of rooms."
如果尝试通过可选链接设置属性,则情况也是如此。上面的“通过可选链接访问属性”中的示例尝试为设置一个address 值john.residence ,即使该residence 属性为nil 。通过可选链接设置属性的任何尝试都会返回type的值Void? ,这使您可以与之进行比较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.")
- }
- // Prints "It was not possible to set the address."
通过可选链接访问下标
您可以使用可选链接尝试从下标中检索和设置可选值上的值,并检查该下标调用是否成功。
笔记
通过可选链访问可选值上的下标时,将问号放在下标的括号之前,而不是之后。可选链接问号总是紧接在表达式的可选部分之后。
下面的示例尝试使用在类上定义的下标检索属性rooms 数组中第一个房间的名称。因为当前为,所以下标调用失败:john.residence Residence john.residence nil
- 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 标括号之前,之后,因为john.residence 这是尝试进行可选链接的可选值。
同样,您可以尝试通过带有可选链接的下标设置新值:
- john.residence?[0] = Room(name: "Bathroom")
此下标设置尝试也失败了,因为residence 当前为nil 。
如果您创建一个实际Residence 实例并将其分配给john.residence ,并Room 在其rooms 数组中包含一个或多个实例,则可以使用Residence 下标rooms 通过可选的链接访问数组中的实际项目:
- 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."
访问可选类型的下标
如果下标返回的是可选类型的值(例如,SwiftDictionary 类型的键下标),则在下标的右括号后面放置一个问号,以链接到其可选的返回值上:
- 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]
上面的示例定义了一个名为的字典testScores ,其中包含两个将键映射String 到Int 值数组的键值对。该示例使用可选链接将"Dave" 数组中的第一项设置为91 ;。使"Bev" 数组中的第一项增加1 ; 并尝试将数组中的第一项设置为的键"Brian" 。前两个调用成功,因为testScores 字典包含"Dave" 和的键"Bev" 。第三次调用失败,因为testScores 字典不包含的键"Brian" 。
链接多个级别的链接
您可以将多个级别的可选链接链接在一起,以深入挖掘模型中更深层的属性,方法和下标。但是,多个级别的可选链接不会为返回值添加更多级别的可选性。
换一种方式:
- 如果您尝试检索的类型不是可选的,则由于可选的链接,它将变为可选的。
-
如果你正在尝试检索类型是已经可选的,它不会变得更因为链接可选。
所以:
-
如果尝试
Int 通过可选的链接检索值,Int? 则无论使用多少级链接,总是会返回an 。
-
同样,如果您尝试
Int? 通过可选的链接检索值,Int? 则无论使用多少级链接,总是会返回an 。
以下示例尝试访问的street 属性address 的residence 属性john 。有2个可选链接的水平在这里使用,以链通过residence 和address 性能,这两者都是可选类型:
- 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 当前的值包含一个有效Residence 实例。但是,john.residence.address 当前的值为nil 。因此,对的调用john.residence?.address?.street 失败。
请注意,在上面的示例中,您尝试检索street 属性的值。此属性的类型为String? 。john.residence?.address?.street 因此String? ,即使除了该属性的基础可选类型之外,还应用了两个级别的可选链接,它的返回值也为。
如果您将实际Address 实例设置为的值john.residence.address ,并为地址的street 属性设置了实际值,则可以street 通过多级可选链访问该属性的值:
- 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.")
- }
- // Prints "John's street name is Laurel Street."
在此示例中,设置的address 属性的尝试john.residence 将成功,因为john.residence 当前的值包含有效Residence 实例。
链接具有可选返回值的方法
前面的示例显示了如何通过可选链接检索可选类型的属性的值。您还可以使用可选链接来调用返回可选类型值的方法,并根据需要链接该方法的返回值。
下面的示例通过可选的链接调用Address 类的buildingIdentifier() 方法。此方法返回type的值String? 。如上所述,在可选链接之后,此方法调用的最终返回类型也是String? :
- 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 doesn't begin with \"The\".")
- }
- }
- // Prints "John's building identifier begins with "The"."
笔记
在上面的例子中,您将可选链接问号后的括号内,因为你要串联上可选的值是buildingIdentifier() 方法的返回值,而不是buildingIdentifier() 方法本身。
|
请发表评论