Swift从入门到精通第十三篇 - 可选项响应链 初识

可选项响应链(学习笔记)

环境Xcode 11.0 beta4 swift 5.1

  • 可选项响应链
    • 可选项响应链是一个用于访问和调用属性、方法、下标的过程,这些属性、方法、下标可能为 nil;如果有值就会调用成功,如果响应链中只要有一处为 nil,则整个链就会失败;Swift 中可选项响应链类似于 Objective-C 中传递消息 nil,但其可用于任何类型,并且可以检查成功不是失败。
  • 可选项作为强制解包一种替代方案
    • 示例

      class Person {
          var residence: Residence?
      }
      // 
      class Residence {
          var numberOfRooms = 1
      }
      let john = Person()
      let roomCount = john.residence!.numberOfRooms
      // this triggers a runtime error
      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."
      // 如果你给residence赋值,结果如下
      john.residence = Residence()
      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)."
  • 为可选项响应链定义模型类
    • 示例代码块
    class Person {
        var residence: Residence?
    }
    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?
    }
    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 let buildingNumber = buildingNumber, let street = street {
                return "\(buildingNumber) \(street)"
            } else if buildingName != nil {
                return buildingName
            } else {
                return 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.")
    }
    // Prints "Unable to retrieve the number of rooms."
    // 通过响应链设置值
    // 由于此之前并没有为residence设值,为nil,因此用someAddress赋值不会成功
    let someAddress = Address()
    someAddress.buildingNumber = "29"
    someAddress.street = "Acacia Road"
    john.residence?.address = someAddress
    // 上面的情况不好观察,下面用函数会比较好观察
    func createAddress() -> Address {
        print("Function was called.")
    //
        let someAddress = Address()
        someAddress.buildingNumber = "29"
        someAddress.street = "Acacia Road"
    //
        return someAddress
    }
    john.residence?.address = createAddress()
    // 可以发现上面的 "Function was called."不会打印
  • 通过可选项调用方法
    • 示例代码块
    // 本例中方法 printNumberOfRooms() 有一个隐式返回值 `Void` (或者 `()` 即空元组)
    // 如果是可选的值调用,则返回的是 `Void?`,不是 `Void`
    // 因此可以用 `if` 语句来判断方法是否调用成功
    // 
    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."
    //
    // 如果属性返回是 `Void?`,也可用同样的判断方法
    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."
  • 通过可选项访问下标
    • ? 放在 [] 之前,总是紧跟表达式之后
    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."
    // `?` 的执行是在紧跟 residence 之后, 在 `[]` 调用之前
    john.residence?[0] = Room(name: "Bathroom")
    // 上面的尝试赋值也会失败,因为 residence 仍然是 nil
    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."
    • 可选类型的下标访问,? 要写在 [] 之后
    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]
  • 连接多层级的响应链
    • 如果要获取的值不是可选项,但由于可选响应链,它将会成为可选项
    • 如果要获取的值是可选项,也不会因为可选项响应链变成多层可选(类似:??, ???)
    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."
    // 如果你给 address 赋一个值
    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."
  • 方法返回可选项的响应链
    • 示例代码块
    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 does not begin with \"The\".")
        }
    }
    // Prints "John's building identifier begins with "The"."
    // 注意:`?` 是加在 `()` 之后因为返回值才是可选项,而不是方法名之后
上一篇:Priest John's Busiest Day (2-sat)


下一篇:Django用mongoengine操作mongodb