Swift - 可选链

  • 简述

    在可能为“nil”的可选项上查询、调用属性、下标和方法的过程被定义为可选项链。可选链接返回两个值 -
    • 如果可选项包含“值”,则调用其相关属性、方法和下标返回值
    • 如果可选项包含“nil”值,则其所有相关属性、方法和下标都返回 nil
    由于对方法、属性和下标的多个查询被组合在一起,一个链的失败将影响整个链并导致“nil”值。
  • 可选链作为强制展开的替代方法

    可选链是在可选值之后用“?”指定的 当可选值返回一些值时调用属性、方法或下标。
    可选链 '?' 访问方法、属性和下标可选链接 '!' 强制展开
    ? 放置在可选值之后以调用属性、方法或下标 !放置在可选值之后以调用属性、方法或下标以强制展开值
    当可选项为 'nil' 时优雅地失败 当可选项为“nil”时,强制解包会触发运行时错误

    使用 '!' 进行可选链接的程序

    
    class ElectionPoll {
       var candidate: Pollbooth?
    }
    lass Pollbooth {
       var name = "MP"
    }
    let cand = ElectionPoll()
    let candname = cand.candidate!.name
    
    当我们使用playground运行上述程序时,我们得到以下结果 -
    
    fatal error: unexpectedly found nil while unwrapping an Optional value
    0 Swift 4 0x0000000103410b68
    llvm::sys::PrintStackTrace(__sFILE*) + 40
    1 Swift 4 0x0000000103411054 SignalHandler(int) + 452
    2 libsystem_platform.dylib 0x00007fff9176af1a _sigtramp + 26
    3 libsystem_platform.dylib 0x000000000000000b _sigtramp + 1854492939
    4 libsystem_platform.dylib 0x00000001074a0214 _sigtramp + 1976783636
    5 Swift 4 0x0000000102a85c39
    llvm::JIT::runFunction(llvm::Function*, std::__1::vector > const&) + 329
    6 Swift 4 0x0000000102d320b3
    llvm::ExecutionEngine::runFunctionAsMain(llvm::Function*,
    std::__1::vector<std::__1::basic_string, std::__1::allocator >,
    std::__1::allocator<std::__1::basic_string, std::__1::allocator > > > const&,
    char const* const*) + 1523
    7 Swift 4 0x000000010296e6ba Swift 4::RunImmediately(Swift
    4::CompilerInstance&, std::__1::vector<std::__1::basic_string,
    std::__1::allocator >, std::__1::allocator<std::__1::basic_string,
    std::__1::allocator > > > const&, Swift 4::IRGenOptions&, Swift 4::SILOptions
    const&) + 1066
    8 Swift 4 0x000000010275764b frontend_main(llvm::ArrayRef,
    char const*, void*) + 5275
    9 Swift 4 0x0000000102754a6d main + 1677
    10 libdyld.dylib 0x00007fff8bb9e5c9 start + 1
    11 libdyld.dylib 0x000000000000000c start + 1950751300
    Stack dump:
    0. Program arguments:
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/
    usr/bin/Swift 4 -frontend -interpret - -target x86_64-apple-darwin14.0.0 -
    target-cpu core2 -sdk
    /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/
    SDKs/MacOSX10.10.sdk -module-name main
    /bin/sh: line 47: 15672 Done cat <<'SWIFT 4'
    import Foundation
    </std::__1::basic_string</std::__1::basic_string</std::__1::basic_string</std::
    __1::basic_string
    
    上述程序将“ElectionPoll”声明为类名,并包含“'candidate”作为成员函数。子类声明为“poll booth”,“name”作为其成员函数,初始化为“MP”。对超类的调用是通过创建一个带有可选的 '!' 的实例 'cand' 来初始化的。由于值未在其基类中声明,因此存储“nil”值,从而通过强制解包过程返回致命错误。

    使用“?”进行可选链接的程序

    
    class ElectionPoll {
       var candidate: Pollbooth?
    }
    class Pollbooth {
       var name = "MP"
    }
    let cand = ElectionPoll()
    if let candname = cand.candidate?.name {
       print("Candidate name is \(candname)")
    } else {
       print("Candidate name cannot be retreived")
    }
    
    当我们使用playground运行上述程序时,我们得到以下结果 -
    
    Candidate name cannot be retreived
    
    上面的程序将“选举投票”声明为类名,并包含“候选人”作为成员函数。子类声明为“投票站”,“名称”作为其成员函数,初始化为“MP”。对超类的调用是通过创建一个带有可选“?”的实例“cand”来初始化的。由于值未在其基类 'nil' 中声明,因此 else 处理程序块在控制台中存储和打印值。
  • 为可选链和访问属性定义模型类

    Swift 4 语言还提供了可选链的概念,将多个子类声明为模型类。这个概念对于定义复杂模型和访问属性、方法和下标子属性非常有用。
    
    class rectangle {
       var print: circle?
    }
    class circle {
       var area = [radius]()
       var cprint: Int {
          return area.count
       }
       subscript(i: Int) -> radius {
          get {
             return area[i]
          }
          set {
             area[i] = newValue
          }
       }
       func circleprint() {
          print("The number of rooms is \(cprint)")
       }
       var rectarea: circumference?
    }
    class radius {
       let radiusname: String
       init(radiusname: String) { self.radiusname = radiusname }
    }
    class circumference {
       var circumName: String?
       var circumNumber: String?
       var street: String?
       func buildingIdentifier() -> String? {
          if circumName != nil {
             return circumName
          } else if circumNumber != nil {
             return circumNumber
          } else {
             return nil
          }
       }
    }
    let rectname = rectangle()
    if let rectarea = rectname.print?.cprint {
       print("Area of rectangle is \(rectarea)")
    } else {
       print("Rectangle Area is not specified")
    }
    
    当我们使用playground运行上述程序时,我们得到以下结果 -
    
    Rectangle Area is not specified
    
  • 通过可选链调用方法

    
    class rectangle {
       var print: circle?
    }
    class circle {
       var area = [radius]()
       var cprint: Int {
          return area.count
       }
       subscript(i: Int) -> radius {
          get {
             return area[i]
          }
          set {
             area[i] = newValue
          }
       }
       func circleprint() {
          print("Area of Circle is: \(cprint)")
       }
       var rectarea: circumference?
    }
    class radius {
       let radiusname: String
       init(radiusname: String) { self.radiusname = radiusname }
    }
    class circumference {
       var circumName: String?
       var circumNumber: String?
       var circumarea: String?
       
       func buildingIdentifier() -> String? {
          if circumName != nil {
             return circumName
          } else if circumNumber != nil {
             return circumNumber
          } else {
             return nil
          }
       }
    }
    let circname = rectangle()
    if circname.print?.circleprint() != nil {
       print("Area of circle is specified)")
    } else {
       print("Area of circle is not specified")
    }
    
    当我们使用playground运行上述程序时,我们得到以下结果 -
    
    Area of circle is not specified
    
    在 circle() 子类中声明的函数 circleprint() 通过创建一个名为 'circname' 的实例来调用。如果函数包含某个值,则该函数将返回一个值,否则它将通过检查语句“if circname.print?.circleprint() != nil”返回一些用户定义的打印消息。
  • 通过可选链访问下标

    可选链用于设置和检索下标值以验证对该下标的调用是否返回一个值。'?放置在下标大括号之前以访问特定下标上的可选值。

    方案一

    
    class rectangle {
       var print: circle?
    }
    class circle {
       var area = [radius]()
       var cprint: Int {
          return area.count
       }
       subscript(i: Int) -> radius {
          get {
             return area[i]
          }
          set {
             area[i] = newValue
          }
       }
       func circleprint() {
          print("The number of rooms is \(cprint)")
       }
       var rectarea: circumference?
    }
    class radius {
       let radiusname: String
       init(radiusname: String) { self.radiusname =  radiusname }
    }
    class circumference {
       var circumName: String?
       var circumNumber: String?
       var circumarea: String?
       
       func buildingIdentifier() -> String? {
          if circumName != nil {
             return circumName
          } else if circumNumber != nil {
             return circumNumber
          } else {
             return nil
          }
       }
    }
    let circname = rectangle()
    if let radiusName = circname.print?[0].radiusname {
       print("The first room name is \(radiusName).")
    } else {
       print("Radius is not specified.")
    }
    
    当我们使用playground运行上述程序时,我们得到以下结果 -
    
    Radius is not specified.
    
    在上述程序中,未指定隶属函数“radiusName”的实例值。因此对函数的程序调用将只返回其他部分,而要返回值,我们必须为特定的隶属函数定义值。

    方案二

    
    class rectangle {
       var print: circle?
    }
    class circle {
       var area = [radius]()
       var cprint: Int {
          return area.count
       }
       subscript(i: Int) -> radius {
          get {
             return area[i]
          }
          set {
             area[i] = newValue
          }
       }
       func circleprint() {
          print("The number of rooms is \(cprint)")
       }
       var rectarea: circumference?
    }
    class radius {
       let radiusname: String
       init(radiusname: String) { self.radiusname = radiusname }
    }
    class circumference {
       var circumName: String?
       var circumNumber: String?
       var circumarea: String?
       
       func buildingIdentifier() -> String? {
          if circumName != nil {
             return circumName
          } else if circumNumber != nil {
             return circumNumber
          } else {
             return nil
          }
       }
    }
    let circname = rectangle()
    circname.print?[0] = radius(radiusname: "Diameter")
    let printing = circle()
    printing.area.append(radius(radiusname: "Units"))
    printing.area.append(radius(radiusname: "Meter"))
    circname.print = printing
    if let radiusName = circname.print?[0].radiusname {
       print("Radius is measured in \(radiusName).")
    } else {
       print("Radius is not specified.")
    }
    
    当我们使用playground运行上述程序时,我们得到以下结果 -
    
    Radius is measured in Units.
    
    在上面的程序中,指定了隶属函数“radiusName”的实例值。因此对函数的程序调用现在将返回值。
  • 访问可选类型的下标

    
    class rectangle {
       var print: circle?
    }
    class circle {
       var area = [radius]()
       var cprint: Int {
          return area.count
       }
       subscript(i: Int) -> radius {
          get {
             return area[i]
          }
          set {
             area[i] = newValue
          }
       }
       func circleprint() {
          print("The number of rooms is \(cprint)")
       }
       var rectarea: circumference?
    }
    class radius {
       let radiusname: String
       init(radiusname: String) { self.radiusname = radiusname }
    }
    class circumference {
       var circumName: String?
       var circumNumber: String?
       var circumarea: String?
       
       func buildingIdentifier() -> String? {
          if circumName != nil {
             return circumName
          } else if circumNumber != nil {
             return circumNumber
          } else {
             return nil
          }
       }
    }
    let circname = rectangle()
    circname.print?[0] = radius(radiusname: "Diameter")
    let printing = circle()
    printing.area.append(radius(radiusname: "Units"))
    printing.area.append(radius(radiusname: "Meter"))
    circname.print = printing
    var area = ["Radius": [35, 45, 78, 101], "Circle": [90, 45, 56]]
    area["Radius"]?[1] = 78
    area["Circle"]?[1]--
    print(area["Radius"]?[0])
    print(area["Radius"]?[1])
    print(area["Radius"]?[2])
    print(area["Radius"]?[3])
    print(area["Circle"]?[0])
    print(area["Circle"]?[1])
    print(area["Circle"]?[2])
    
    当我们使用playground运行上述程序时,我们得到以下结果 -
    
    Optional(35)
    Optional(78)
    Optional(78)
    Optional(101)
    Optional(90)
    Optional(44)
    Optional(56)
    
    下标的可选值可以通过引用它们的下标值来访问。它可以作为下标[0]、下标[1]等访问。“半径”的默认下标值首先被分配为[35、45、78、101]和“圆”[90、45、56]] . 然后将下标值更改为 Radius[0] 为 78,Circle[1] 为 45。
  • 链接多级链接

    多个子类也可以通过可选链接与其超类方法、属性和下标链接。
    可以链接多个可选链接 -
    如果检索类型不是可选的,可选链将返回一个可选值。例如,如果 String 通过可选链接,它将返回 String?价值
    
    class rectangle {
       var print: circle?
    }
    class circle {
       var area = [radius]()
       var cprint: Int {
          return area.count
       }
       subscript(i: Int) -> radius {
          get {
             return area[i]
          }
          set {
             area[i] = newValue
          }
       }
       func circleprint() {
          print("The number of rooms is \(cprint)")
       }
       var rectarea: circumference?
    }
    class radius {
       let radiusname: String
       init(radiusname: String) { self.radiusname = radiusname }
    }
    class circumference {
       var circumName: String?
       var circumNumber: String?
       var circumarea: String?
       func buildingIdentifier() -> String? {
          if circumName != nil {
             return circumName
          } else if circumNumber != nil {
             return circumNumber
          } else {
             return nil
          }
       }
    }
    let circname = rectangle()
    if let radiusName = circname.print?[0].radiusname {
       print("The first room name is \(radiusName).")
    } else {
       print("Radius is not specified.")
    }
    
    当我们使用playground运行上述程序时,我们得到以下结果 -
    
    Radius is not specified.
    
    在上面的程序中,没有指定隶属函数“radiusName”的实例值。因此,对函数的程序调用将只返回 else 部分,而要返回值,我们必须为特定的隶属函数定义值。
    如果检索类型已经是可选的,则可选链也将返回一个可选值。例如,如果字符串?是否通过可选链接访问它会返回字符串?价值..
    
    class rectangle {
       var print: circle?
    }
    class circle {
       var area = [radius]()
       var cprint: Int {
          return area.count
       }
       subscript(i: Int) -> radius {
          get {
             return area[i]
          }
          set {
             area[i] = newValue
          }
       }
       func circleprint() {
          print("The number of rooms is \(cprint)")
       }
       var rectarea: circumference?
    }
    class radius {
       let radiusname: String
       init(radiusname: String) { self.radiusname = radiusname }
    }
    class circumference {
       var circumName: String?
       var circumNumber: String?
       var circumarea: String?
       
       func buildingIdentifier() -> String? {
          if circumName != nil {
             return circumName
          } else if circumNumber != nil {
             return circumNumber
          } else {
             return nil
          }
       }
    }
    let circname = rectangle()
    circname.print?[0] = radius(radiusname: "Diameter")
    let printing = circle()
    printing.area.append(radius(radiusname: "Units"))
    printing.area.append(radius(radiusname: "Meter"))
    circname.print = printing
    if let radiusName = circname.print?[0].radiusname {
       print("Radius is measured in \(radiusName).")
    } else {
       print("Radius is not specified.")
    }
    
    当我们使用playground运行上述程序时,我们得到以下结果 -
    
    Radius is measured in Units.
    
    在上面的程序中,指定了隶属函数“radiusName”的实例值。因此,对该函数的程序调用现在将返回值。
  • 链接具有可选返回值的方法

    可选链也用于访问子类定义的方法。
    
    class rectangle {
       var print: circle?
    }
    class circle {
       var area = [radius]()
       var cprint: Int {
          return area.count
       }
       subscript(i: Int) -> radius {
          get {
             return area[i]
          }
          set {
             area[i] = newValue
          }
       }
       func circleprint() {
          print("Area of Circle is: \(cprint)")
       }
       var rectarea: circumference?
    }
    class radius {
       let radiusname: String
       init(radiusname: String) { self.radiusname = radiusname }
    }
    class circumference {
       var circumName: String?
       var circumNumber: String?
       var circumarea: String?
       
       func buildingIdentifier() -> String? {
          if circumName != nil {
             return circumName
          } else if circumNumber != nil {
             return circumNumber
          } else {
             return nil
          }
       }
    }
    let circname = rectangle()
    if circname.print?.circleprint() != nil {
       print("Area of circle is specified)")
    } else {
       print("Area of circle is not specified")
    }
    
    当我们使用playground运行上述程序时,我们得到以下结果 -
    
    Area of circle is not specified