• Swift5.3 语言指南(十八) 可选链接


    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
    ➤微信公众号:山青咏芝(shanqingyongzhi)
    ➤博客园地址:山青咏芝(https://www.cnblogs.com/strengthen/
    ➤GitHub地址:https://github.com/strengthen/LeetCode
    ➤原文地址:https://www.cnblogs.com/strengthen/p/9739421.html 
    ➤如果链接不是山青咏芝的博客园地址,则可能是爬取作者的文章。
    ➤原文已修改更新!强烈建议点击原文地址阅读!支持作者!支持原创!
    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

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

    注意

    Swift中的可选链接类似于nilObjective-C中的消息传递,但是它适用于任何类型,并且可以检查成功或失败。

    可选链接作为强制展开的替代方法

    您可以通过在?要调用其属性,方法或下标的可选值之后放置问号()来指定可选链,如果可选值不是- nil这非常类似于将感叹号(!放在可选值之后以强制展开其值。主要区别在于,当可选值为时,可选链接会正常失败nil,而当可选值为时,强制展开会触发运行时错误nil

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

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

    接下来的几个代码片段演示了可选链接与强制展开如何不同,并使您能够检查是否成功。

    首先,定义了两个类PersonResidence

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

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

    如果创建新Person实例,则其residence属性默认为nil,这是可选的。在下面的代码中,johnresidence属性值为nil

    1. let john = Person()

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

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

    上面的代码在john.residence为非nil时成功,并将设置roomCountInt包含适当数量房间值。但是,如上所述,此代码始终会在residenceis 时触发运行时错误nil

    可选链接为访问的值提供了另一种方法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 residence存在的值

    由于尝试访问numberOfRooms可能会失败,因此可选的链接尝试将返回type Int?或“ optional Int”的值。如上面的示例中,residence为is时nil,此可选选项Int也将为nil,以反映无法访问的事实numberOfRooms可选的Int是通过可选的结合解开的整数并分配非可选值到所访问roomCount的变量。

    请注意,即使这numberOfRooms是非可选的,也是如此Int通过可选链查询它的事实意味着,对的调用numberOfRooms将始终返回Int?而不是Int

    您可以将Residence实例分配john.residence,以使其不再具有nil值:

    1. 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)."

    定义可选链接的模型类

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

    下面的代码段定义了四个模型类,可用于后续的几个示例中,包括多级可选链接的示例。这些类通过添加类以及相关的属性,方法和下标,从上面扩展了Personand Residence模型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属性实现为计算属性,而不是存储属性。计算的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此类具有type的三个可选属性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,如果它有一个值,或buildingNumber与级联street如果两者都具有值,或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. let someAddress = Address()
    4. someAddress.buildingNumber = "29"
    5. someAddress.street = "Acacia Road"
    6. return someAddress
    7. }
    8. 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这是尝试进行可选链接的可选值。

    同样,您可以尝试通过带有可选链接的下标设置新值:

    1. 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. if let firstRoomName = john.residence?[0].name {
    6. print("The first room name is (firstRoomName).")
    7. } else {
    8. print("Unable to retrieve the first room name.")
    9. }
    10. // 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?则无论使用多少级链接,总是会返回an 
    • 同样,如果您尝试Int?通过可选的链接检索值,Int?则无论使用多少级链接,总是会返回an 

    以下示例尝试访问的street属性addressresidence属性john2个可选链接的水平在这里使用,以链通过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实例设置为的值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. if let johnsStreet = john.residence?.address?.street {
    6. print("John's street name is (johnsStreet).")
    7. } else {
    8. print("Unable to retrieve the address.")
    9. }
    10. // 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()方法本身。

     

  • 相关阅读:
    (转载)delphi文件流
    一个不敢妄称自己是程序员的半拉子编码员的随想
    Xamarin.iOS使用极光JPush进行推送
    Xamarin.IOS问题记录——项目属性里IOS Bundle Signing 配置文件选项没有对应的配置文件选择
    Xamarin问题记录
    Unity3D笔记
    C#Xml To Class生成器
    WPF Mahapps.Metro 设置主题样式
    WPF画N角芒星,正N角星
    WPFPath素材
  • 原文地址:https://www.cnblogs.com/strengthen/p/9739421.html
Copyright © 2020-2023  润新知