zoukankan      html  css  js  c++  java
  • Swift 可选型

    1、可选型

    • Swift 语言为我们提供了一种全新的、更加安全的类型 “可选型”。可选型是使用范型枚举的形式来组织的,也就是说此特性可以运用于所有的类型、结构体、类或者其他复杂数据类型。

      • Swift 中的可选型会在编译时就去检查某些可能发生在运行时的错误,可以提高代码的质量。
      • 可选型用于某些不确定是否有值的情况,其有两个值,即 “具体的值” 和 nil
      • 当一个变量、常量或者其他类中存储有值的时候返回里面存储的值,没有值的时候返回 nil(表示空值)。
      • Swift 中把一个变量定义为可选型,在没有赋值的情况下它会被默认赋值为 nil
      • 在 OC 中 nil 表示的是一个指向不存在对象的指针,而 Swift 中表示空的关键字为 nil,没有其他含义。
      • 在 Swift 中不能给一个可选型之外的类型赋值为 nil
    • 可选的声明

      • 可选的标准声明形式,直接在程序中使用变量类型紧跟 ?

        var age: Int?
        
      • 可选的显式声明形式

        var age: Optional<Int>
        
    • 相比单纯的 nil,使用可选型封装的 nil 给了开发者更合理的机会去处理空值:选择忽略空值,或者为可能出现空值的对象赋上默认值(使用 ?? 操作符)。

    • 在条件足够完备的时候,尽量不要定义一个可选型,比如网络通信是一个复杂的环境,很多因素是不可控的,所以如果程序中需要通过网络采集数据,那么数据的借口可能是可选型的,最好不要把你的数据模型也声明成可选型,虽然这样可以减少网络数据到本地数据映射时的代码量。更好的方法是对可选型使用 ?? 赋值,这样在网络数据返回空值的时候,你的数据模型对应的属性就是初始化时设置的默认值。

    2、强制解包可选

    • Swift 中把一个变量定义为可选型,在没有赋值的情况下它会被默认赋值为 nil,所以即便定义了 age 的时候不赋值,它也已经被初始化了,它的值是 nil,直到你通过赋值更改它的值。

      var age: Int?
      age = Int("12")
      print("age is (age)")
      
      • 在输出语句中我们得到 age 的值显示为 Optional(12)Optional 代表 “可选”,age 的当前值为一个整数类型的可选型。
      • 在实际开发中我们真正需要的是括号中的 12,要想获得这个 12,就需要使用 “解包” 操作。
    • 解包是针对于可选类型的变量操作,当我们确定一个可选类型的值不为 nil 的时候,可以使用变量名紧跟 ! 的方式强制获取类型可选中的值,从而省略判断步骤,这种机制叫做 “强制解包可选”。

      var age: Int?
      age = Int("12")
      print("age is (age!)")
      
      • 此时在输出语句中我们得到 age 的值显示为 12。
    • 但是如果这个变量中没有值,那么在解包的时候程序就会崩溃。

    3、可选绑定

    • 想比于强制解包,Swift 提供了一种更安全的解包方式 “可选绑定”。可选绑定有两种格式 if-letguard-let-else

    • 可选绑定本身代表了两个步骤:解包和拷贝,如果你需要一个解包后的变量拷贝的话,可以使用 if-varguard-var)的语法。

      let firstStr: String? = "hello"
      let secondStr: String? = "world"
      
      if var fs = firstStr, let ss = secondStr {
          
          print(fs + " " + ss)        // hello world
          
          fs = "hi"                   // 修改
          
          print(fs + " " + ss)        // hi world
      }
      

    3.1 if-let 格式

    • if-let 格式

      var age: Int?
      
      if let value = age {
      
          // 在内部使用解包后的值
          print("(value)")
      }
      
      • 如果 age 有值,对 age 解包并给其一个 “别名” value,在 if 后的大括号对中 vaule 才有效果。
      • 如果 age 无值,即为 nil 时,那么大括号对中的代码不会执行,使可选解包程序不会崩溃。

    3.2 guard-let-else 格式

    • guard-let-else 格式

      var age: Int?
      
      guard let value = age else {
      
          // 终止方法
          return
      }
      
      // 在外部使用解包后的值
      print("(value)")
      
      • guard-let-else 首先处理 age 为 nil 时的情况,在此种情况下必须在大括号中使用 return 或者 break 提前终止代码。
      • 如果 age 有值,那么 age 的值也会被保存在 “别名” value 中,不管有多少个 guard-let-else,“别名” value 的作用域都在最外层。

    3.3 可选绑定嵌套

    • 在 Swift 1.2 之前

      if let a = b {
          
          if let c = d {
              
              ...
          }
      }
      
    • 在 Swift 1.2 之后,可以把所有的可选绑定放到一个 if-let 中,使用逗号隔开。

      if let a = b, c = d, ... {
          
      }
      
    • 在 Swift 3.0 之后,可选绑定和普通的判断语句可以混用到一个 if 语句中,所以每一次可选绑定都需要用 let 关键字来声明。

      if let a = b, let c = d, ... {
          
      }
      

    4、隐式解包

    • 隐式解包是一种特殊的可选型,在系统中由于一些历史原因出现这种类型,但是我们自己最好不要定义这样的类型。

      • 将可选变量声明中的 ? 改为 ! 来标注一个隐式解包可选。
      • 隐式解包可选用于一个确定会有值的可选类型实例声明。
      var age: Int! = 18
      
    • 隐式解包的类型可以和普通的类型进行运算,此时它会被解包,结果返回的也是非可选型的类型。

      let a: Int! = 3
      
      let b = a + 1           // b 的类型是 Int,值为 4
      
    • 在 Swift 2.2 中,隐式解包的实例传递的对象的类型依旧是隐式解包类型的,并且和普通类型所组成的数组也会被推断为隐式解包的数组类型。

      let a: Int! = 3
      
      let c = a               // c 的类型依旧为 Int!
      let arr = [1, a]        // 数组 arr 的类型是 [Int!] 类型的
      
    • 在 Swift 3.0 及以后,隐式解包的类型在传递时都会被当作普通的可选型,只有在运算这种需要解包的时候才会被解包。

      let a: Int! = 3
      
      let c = a               // c 的类型依旧为 Int?
      let arr = [1, a]        // 数组 arr 的类型是 [Int?] 类型的
      
    • 在 Swift 工程中,最常见的隐式解包就是 IBOutlet

      IBOutlet weak var dateLabel: UILabel!
      
      • IBActionIBOutlet 允许你的代码和 Interface Builder 中的元素进行交互,通过隐式解包的声明方式。
      • 当 Swift 代码对象初始化之后,storyboard 中的元素会在运行时与 IBOutlet 对象对接,这也是不能在 prepareForSegue 方法中访问目标控制器中的 IBOutlet 元素的原因,因为在初始化时会造成对 nil 的解包导致程序崩溃。

    5、可选运算符

    • 可选运算符 ?? 之前是一个可选型,?? 之后是一个非可选型的值。?? 操作符自带解包功能,在赋值时 ?? 会检查其之前的可选型。

      var age: Int?
      var value = age ?? -1
      
      • 如果可选型不为 nil 则将其解包并返回。
      • 如果为 nil,则不会返回 nil,此时返回 ?? 之后的非可选型的值。

    6、可选链

    • 针对可选型结果中仍有可选型的情况,可以使用可选链操作,将可选的调用使用 ? 连接在一起形成一个链,如果可选链中任何一个部位的可选型是空值,则可选链会中断,而不会引发强制解包可选时发生的错误。

      • 除了可以对可选型中的属性进行判空外,可选链更适用于可选型中的方法调用。
      • 如果可选链末尾是一个方法,那么当链条提前中断的时候,该方法不会执行。
      • 可选链可以多层可选(嵌套)。
    • 有一个类 Student,里面有两个属性 nameage,它们的类型是可选型。

      class Student {
          var name: String?
          var age: Int?
      }
      
      func example(code: String) -> Student? {
          
          if code == "xm" {
              
              let xiaoming: Student = Student()
              xiaoming.name = "XiaoMing"
              xiaoming.age = 12
              return xiaoming
              
          } else if code == "xg" {
              
              let xiaogang: Student = Student()
              xiaogang.name = "XiaoGang"
              xiaogang.age = 13
              return xiaogang
          }
          
          return nil
      }
      
      // 使用可选绑定来判断
      if let student = example(code: "xm") {
          
          // 使用可选绑定来判断
          if let age = student.age {
              
              let year = 18 - age
              print(year)
          }
      }
      
    • 上面的代码运行没有问题,但是使用了两个可选绑定,因此可以使用一个 if let 即使用可选链来化简。

      // 使用可选链来判断
      if let age = example(code: "xm")?.age {
          
          let year = 18 - age
          print(year)
      }
      
    • 可选绑定与可选链的使用

      • 如果需要频繁的使用一个可选型的值,那么可选绑定的代价是和算的。
      • 如果需要在某个可选型有值的时候调用它内部的方法,如果是空值则不调用,那么使用可选链的方式会更加简单。
    • 可选绑定的理念是忽略空值,而可选链中可以使用空值,比如当空值出现时使用 ?? 把空值转换成常量。

      let age = example(code: "lw")?.age ?? 0
      

    7、Map 和 FlatMap

    • 可选型中有两个非常实用的方法 mapflatMap,当你确定需要返回一个可选型,并且希望当可选型不为空时,对可选型的值做某些处理后再返回,可以选用 map

    • 可选型的 mapflatMap 的定义如下

      public func map<U>(_ transform: (Wrapped) throws -> U) rethrows -> U?
      
      public func flatMap<U>(_ transform: (Wrapped) throws -> U?) rethrows -> U?
      
    • 在不确定某个学生的年龄是否有值的情况下,我们定义一个 isAdult 方法,它的作用是判断那些有年龄信息的学生的年龄是否成人,对于年龄信息未登记的学生,则返回 nil

      func isAdult(stu: Student) -> Bool? {
          
          let result = stu.age.map {
              $0 >= 18 ? true : false
          }
          
          return result
      }
      
      let xm = Student()
      xm.age = 3
      xm.name = "XiaoMing"
      
      print(isAdult(stu: xm) ?? -1)       // false
      
      • 当某个学生的年龄存在时,参数的 map 方法才会执行。
      • 和可选绑定一样,map 方法体中的参数已经默认解包了,所以这里的 $0 即指 stu.age 解包后的值,是 Int 类型的。
    • 如果使用 map 时,map 方法体中的返回值本身也是一个可选型,则返回结果会出现多重可选型。

      let result = example(code: "xm").map {
          
          isAdult(stu: $0)
      }
      
      • 由于在 map 方法体中的方法 isAdult 返回值也是可选型的,所以 result 的类型是 Bool??,在使用时我们被迫需要做两次空值判断,而这些操作并没有意义,我们关心的只是结果。
    • flatMap 会把多重可选型化简成一个可选型。

      let result = example(code: "xm").flatMap {
          
          isAdult(stu: $0)
      }
      
  • 相关阅读:
    头像切换封装
    JSON数据的序列化方法
    HTML5 LocalStorage 本地存储
    onhashchange实现下一页与上一页功能,并且实现当前页面刷新时停留在当前页面
    提交页面可输入的数据-----XSS漏洞
    js冒泡排序
    html实现将网页页面分享到微信朋友圈添加缩略图图片的方法
    js常用正则表达式
    C 传递指针给函数
    C 指向指针的指针
  • 原文地址:https://www.cnblogs.com/QianChia/p/8628201.html
Copyright © 2011-2022 走看看