加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

Swift4.2语言规范(十八) 可选链接

发布时间:2020-12-14 05:11:35 所属栏目:百科 来源:网络整理
导读:可选链接 是一个查询和调用当前可选的可选项的属性,方法和下标的过程 nil 。 如果optional包含值,则属性,方法或下标调用成功;? 如果是可选的 nil ,则返回属性,方法或下标调用 nil 。 多个查询可以链接在一起,如果链中的任何链接,整个链都会正常失败 n

可选链接是一个查询和调用当前可选的可选项的属性,方法和下标的过程nil如果optional包含值,则属性,方法或下标调用成功;?如果是可选的nil,则返回属性,方法或下标调用nil多个查询可以链接在一起,如果链中的任何链接,整个链都会正常失败nil

注意

Swift中的可选链接类似于nilObjective-C中的消息传递,但其方式适用于任何类型,并且可以检查其成功与否。

可选链接作为强制解包的替代方法

您可以通过在可选值之后放置一个问号(?)来指定可选链接,如果可选值为非,则在该值上调用属性,方法或下标nil这与!在可选值之后放置感叹号(!)以强制展开其值非常相似主要区别在于可选链接在可选项为nil时导致失败,而强制解包在可选项时触发运行时错误nil

要反映可以在nil上调用可选链接的事实,可选链接调用的结果始终是可选值,即使要查询的属性,方法或下标返回非可选值。您可以使用此可选返回值来检查可选链接调用是否成功(返回的可选项包含值),或者由于nil链中值(返回的可选值是nil而未成功

具体来说,可选链接调用的结果与预期返回值的类型相同,但包含在可选中。通常返回an的属性IntInt?在通过可选链接访问时返回

接下来的几个代码片段演示了可选链接与强制解包的区别,并使您能够检查是否成功。

首先,调用PersonResidence定义了两个类

1 class Person {
2     var residence: Residence?
3 }
4 
5 class Residence {
6     var numberOfRooms = 1
7 }

Residence实例有一个Int名为的属性numberOfRooms,默认值为1Person实例具有residence类型的可选属性Residence?

如果您创建一个新Person实例,则其residence属性默认初始化为nil,因为它是可选的。在下面的代码,john具有residence的属性值nil

let john = Person()

如果您尝试访问numberOfRooms此人的属性residence,通过在residence强制residence解除其值后放置感叹号,则会触发运行时错误,因为没有值要解包:

1 let roomCount = john.residence!.numberOfRooms
2 // this triggers a runtime error

如果john.residence具有非nil值,则上面的代码将成功,并将设置roomCountInt包含适当房间数的值。然而,当该代码始终触发运行时错误residencenil,如上述所示。

可选链接提供了一种访问其值的替代方法numberOfRooms要使用可选链接,请使用问号代替感叹号:

1 if let roomCount = john.residence?.numberOfRooms {
2     print("John‘s residence has (roomCount) room(s).")
3 } else {
4     print("Unable to retrieve the number of rooms.")
5 }
6 // Prints "Unable to retrieve the number of rooms."

这告诉Swift在可选residence属性上“链”?并检索numberOfRoomsif?residenceexists?的值

由于尝试访问numberOfRooms可能会失败,因此可选的链接尝试返回值类型Int?或“可选Int”。如果residencenil,如上面的例子中,这个可选的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

1 if let roomCount = john.residence?.numberOfRooms {
2     print("John‘s residence has (roomCount) room(s).")
3 } else {
4     print("Unable to retrieve the number of rooms.")
5 }
6 // Prints "John‘s residence has 1 room(s)."

为可选链接定义模型类

您可以使用可选链接来调用超过一级深度的属性,方法和下标。这使您可以深入查看相互关联类型的复杂模型中的子属性,并检查是否可以访问这些子属性上的属性,方法和下标。

下面的代码片段定义了四个模型类,用于几个后续示例,包括多级可选链接的示例。这些类在所述展开PersonResidence通过添加模型从上方RoomAddress类,具有相关联的属性,方法,和下标。

Person类以同样的方式前的定义:

1 class Person {
2     var residence: Residence?
3 }

Residence门课比以前更复杂。这次,Residence该类定义了一个名为的变量属性rooms,该属性使用类型为空的数组进行初始化[Room]

 1 class Residence {
 2     var rooms = [Room]()
 3     var numberOfRooms: Int {
 4         return rooms.count
 5     }
 6     subscript(i: Int) -> Room {
 7         get {
 8             return rooms[i]
 9         }
10         set {
11             rooms[i] = newValue
12         }
13     }
14     func printNumberOfRooms() {
15         print("The number of rooms is (numberOfRooms)")
16     }
17     var address: Address?
18 }

因为此版本的Residence存储是一个Room实例数组,所以它的numberOfRooms属性实现为计算属性,而不是存储属性。computed?numberOfRooms属性只是countrooms数组中返回属性的值

作为访问其rooms数组的快捷方式,此版本Residence提供了一个读写下标,可以在rooms阵列中请求的索引处提供对房间的访问

此版本Residence还提供了一种名为的方法printNumberOfRooms,它只是打印住宅中的房间数量。

最后,Residence定义一个名为的可选属性address,其类型为Address?Address此属性类类型定义如下。

Room用于类rooms阵列是简单的类与一种属性调用name,以及一个初始值设定到该属性设置为一个合适的房间名称:

1 class Room {
2     let name: String
3     init(name: String) { self.name = name }
4 }

调用此模型中的最后一个类Address该类有三个可选的类型属性String?前两个属性buildingNamebuildingNumber,是将特定建筑物识别为地址一部分的替代方法。第三个属性street用于为该地址命名街道:

 1 class Address {
 2     var buildingName: String?
 3     var buildingNumber: String?
 4     var street: String?
 5     func buildingIdentifier() -> String? {
 6         if let buildingNumber = buildingNumber,let street = street {
 7             return "(buildingNumber) (street)"
 8         } else if buildingName != nil {
 9             return buildingName
10         } else {
11             return nil
12         }
13     }
14 }

所述Address类还提供了一个名为方法buildingIdentifier(),其具有的返回类型String?此方法检查地址的属性,buildingName如果有值,则返回buildingNumberstreet如果两者都有值,则返回连接nil否则返回。

通过可选链接访问属性

可选链接作为强制解包的替代方法所示,您可以使用可选链接访问可选值的属性,并检查该属性访问是否成功。

使用上面定义的类来创建新Person实例,并尝试numberOfRooms像以前一样访问其属性:

1 let john = Person()
2 if let roomCount = john.residence?.numberOfRooms {
3     print("John‘s residence has (roomCount) room(s).")
4 } else {
5     print("Unable to retrieve the number of rooms.")
6 }
7 // Prints "Unable to retrieve the number of rooms."

由于john.residencenil,这种可选的链接调用以同样的方式和以前失败。

您还可以尝试通过可选链接设置属性的值:

1 let someAddress = Address()
2 someAddress.buildingNumber = "29"
3 someAddress.street = "Acacia Road"
4 john.residence?.address = someAddress

在此示例中,尝试设置address属性john.residence将失败,因为john.residence当前nil

赋值是可选链接的一部分,这意味着不会对=运算符右侧的代码进行求值。在前面的示例中,不容易看到someAddress永远不会评估,因为访问常量没有任何副作用。下面的列表执行相同的分配,但它使用一个函数来创建地址。该函数在返回值之前打印“函数被调用”,这样可以查看=操作符的右侧是否已被评估。

 1 func createAddress() -> Address {
 2     print("Function was called.")
 3 
 4     let someAddress = Address()
 5     someAddress.buildingNumber = "29"
 6     someAddress.street = "Acacia Road"
 7 
 8     return someAddress
 9 }
10 john.residence?.address = createAddress()

您可以判断该createAddress()函数未被调用,因为没有打印任何内容。

通过可选链接调用方法

您可以使用可选链接在可选值上调用方法,并检查该方法调用是否成功。即使该方法没有定义返回值,也可以执行此操作。

printNumberOfRooms()对方法Residence类打印的当前值numberOfRooms以下是该方法的外观:

1 func printNumberOfRooms() {
2     print("The number of rooms is (numberOfRooms)")
3 }

此方法未指定返回类型。但是,没有返回类型的函数和方法具有隐式返回类型Void,如函数无返回值中所述这意味着它们返回值()或空元组。

如果在带有可选链接的可选值上调用此方法,则方法的返回类型将Void?不是Void,因为在通过可选链接调用时返回值始终是可选类型。这使您可以使用if语句来检查是否可以调用该printNumberOfRooms()方法,即使该方法本身不定义返回值。比较来自printNumberOfRooms调用的返回值,nil以查看方法调用是否成功:

1 if john.residence?.printNumberOfRooms() != nil {
2     print("It was possible to print the number of rooms.")
3 } else {
4     print("It was not possible to print the number of rooms.")
5 }
6 // Prints "It was not possible to print the number of rooms."

如果您尝试通过可选链接设置属性,情况也是如此。通过可选链接访问属性中的上述示例尝试设置addressjohn.residence,即使该residence属性为nil任何通过可选链接设置属性的尝试都会返回type值Void?,这使您可以比较nil以查看属性是否已成功设置:

1 if (john.residence?.address = someAddress) != nil {
2     print("It was possible to set the address.")
3 } else {
4     print("It was not possible to set the address.")
5 }
6 // Prints "It was not possible to set the address."

通过可选链接访问下标

您可以使用可选链接尝试从可选值的下标中检索和设置值,并检查该下标调用是否成功。

注意

通过可选链接访问可选值的下标时,将问号放在下标括号之前,而不是之后。可选的链接问号始终紧跟在表达式的可选部分之后。

下面的示例尝试使用上定义的下标检索属性rooms数组中第一个房间的名称因为当前,下标调用失败:john.residenceResidencejohn.residencenil

1 if let firstRoomName = john.residence?[0].name {
2     print("The first room name is (firstRoomName).")
3 } else {
4     print("Unable to retrieve the first room name.")
5 }
6 // 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通过可选链接访问数组中的实际项

 1 let johnsHouse = Residence()
 2 johnsHouse.rooms.append(Room(name: "Living Room"))
 3 johnsHouse.rooms.append(Room(name: "Kitchen"))
 4 john.residence = johnsHouse
 5 
 6 if let firstRoomName = john.residence?[0].name {
 7     print("The first room name is (firstRoomName).")
 8 } else {
 9     print("Unable to retrieve the first room name.")
10 }
11 // Prints "The first room name is Living Room."

访问可选类型的下标

如果下标返回一个可选类型的值 - 例如Swift?Dictionary类型的键下标 - 在下标的右括号后面一个问号以链接其可选的返回值:

1 var testScores = ["Dave": [86,82,84],"Bev": [79,94,81]]
2 testScores["Dave"]?[0] = 91
3 testScores["Bev"]?[0] += 1
4 testScores["Brian"]?[0] = 72
5 // the "Dave" array is now [91,82,84] and the "Bev" array is now [80,94,81]

上面的示例定义了一个名为的字典testScores,其中包含两个将键映射StringInt数组的键值对该示例使用可选链接将"Dave"数组中的第一项设置91;?通过增加"Bev"数组中的第一项1;?并尝试在数组中为键的第一项设置"Brian"前两个调用成功,因为testScores字典包含"Dave"和的"Bev"第三个调用失败,因为testScores字典不包含密钥"Brian"

链接多级链接

您可以将多个级别的可选链接链接在一起,以深入查看模型中更深层次的属性,方法和下标。但是,多级可选链接不会为返回值添加更多级别的可选性。

换一种方式:

  • 如果您尝试检索的类型不是可选的,则由于可选链接,它将成为可选类型。
  • 如果您尝试检索的类型已经可选的,则由于链接,它不会变得更加可选。

因此:

  • 如果尝试Int通过可选链接检索值,Int?则无论使用多少级别的链接,都始终返回a。
  • 同样,如果您尝试Int?通过可选链接检索值,Int?则无论使用多少级别的链接,都始终返回a。

下面的示例尝试访问street属性的address属性的residence属性john这里有两个级别的可选链接,用于链接residenceaddress属性,两者都是可选类型:

1 if let johnsStreet = john.residence?.address?.street {
2     print("John‘s street name is (johnsStreet).")
3 } else {
4     print("Unable to retrieve the address.")
5 }
6 // 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实例设置为for的值john.residence.address,并为地址的street属性设置实际值,则可以street通过多级可选链接访问该属性的值

 1 let johnsAddress = Address()
 2 johnsAddress.buildingName = "The Larches"
 3 johnsAddress.street = "Laurel Street"
 4 john.residence?.address = johnsAddress
 5 
 6 if let johnsStreet = john.residence?.address?.street {
 7     print("John‘s street name is (johnsStreet).")
 8 } else {
 9     print("Unable to retrieve the address.")
10 }
11 // Prints "John‘s street name is Laurel Street."

在此示例中,尝试设置address属性john.residence将成功,因为当前值john.residence包含有效Residence实例。

使用可选返回值链接方法

前面的示例演示如何通过可选链接检索可选类型的属性的值。您还可以使用可选链接来调用返回可选类型值的方法,并在需要时链接该方法的返回值。

下面的示例通过可选链接调用Address类的buildingIdentifier()方法。此方法返回type的值String?如上所述,可选链接后此方法调用的最终返回类型还包括String?

1 if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {
2     print("John‘s building identifier is (buildingIdentifier).")
3 }
4 // Prints "John‘s building identifier is The Larches."

如果你想在这个方法的返回值进行进一步的可选链接,将链接可选问号后,该方法的括号:

1 if let beginsWithThe =
2     john.residence?.address?.buildingIdentifier()?.hasPrefix("The") {
3     if beginsWithThe {
4         print("John‘s building identifier begins with "The".")
5     } else {
6         print("John‘s building identifier does not begin with "The".")
7     }
8 }
9 // Prints "John‘s building identifier begins with "The"."

注意

在上面的例子中,您将可选链接问号的括号内,因为你要串联上可选的值是buildingIdentifier()方法的返回值,而不是buildingIdentifier()方法本身。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读