1、可选型

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

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

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

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

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

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

2、强制解包可选

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

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

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

3、可选绑定

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

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

    1. let firstStr: String? = "hello"
    2. let secondStr: String? = "world"
    3. if var fs = firstStr, let ss = secondStr {
    4. print(fs + " " + ss) // hello world
    5. fs = "hi" // 修改
    6. print(fs + " " + ss) // hi world
    7. }

3.1 if-let 格式

  • if-let 格式

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

3.2 guard-let-else 格式

  • guard-let-else 格式

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

3.3 可选绑定嵌套

  • 在 Swift 1.2 之前

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

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

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

4、隐式解包

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

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

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

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

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

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

5、可选运算符

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

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

6、可选链

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

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

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

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

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

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

7、Map 和 FlatMap

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

  • 可选型的 mapflatMap 的定义如下

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

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

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

    1. let result = example(code: "xm").flatMap {
    2. isAdult(stu: $0)
    3. }

Swift 可选型的更多相关文章

  1. Swift中可选型的Optional Chaining 和 Nil-Coalesce(Swift2.1)

    /* 下面是介绍Optional Chaining 和 Nil-Coalesce */ // Optional Chaining (可选链) if let errorMessage = errorMe ...

  2. 重新认识Swift中的可选型(Swift2.1)

    //: Playground - noun: a place where people can play import UIKit /* Swift中nil代表是是另外一种类型, 而不像OC那样, 任 ...

  3. swift 进阶笔记 (一) —— 可选型

    swift定义可选型的时候,要用"?",可是在swift的标准库中,可选型的定义是Optional<T>,"? "仅仅是个简写形式. var myN ...

  4. swift中为什么要创造出可选型?

    (1)因为nil这个东西,swift中没有就是没有.  Int? 叫 整型可选型,如果不提前声明,直接赋值变量 nil会报错 . 可以将Int赋值给Int?   ,但是不能将Int?赋值给Int . ...

  5. Swift隐式可选型简单介绍

    /* 隐式可选型 */ // 隐式可选型同样可以赋值为nil, 而且在后面对这个变量的使用也可以不用进行解包 var value: String! = nil // print(value) 这行代码 ...

  6. Swif - 可选型

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 13.0px Menlo; color: #4dbf56 } p.p2 { margin: 0.0px 0. ...

  7. [转载] 对象存储(2):OpenStack Swift——概念、架构与规模部署

    原文: http://www.testlab.com.cn/Index/article/id/1085.html#rd?sukey=fc78a68049a14bb228cb2742bdec2b9498 ...

  8. 转 : React Native 开发之 IDE 选型和配置

    转:https://mp.weixin.qq.com/s?__biz=MzA3ODg4MDk0Ng==&mid=2651112392&idx=1&sn=135e29ddde30 ...

  9. Swift - 41 - swift1.2新特性(1)

    更简洁的if-let import UIKit func attack(name: String, enemyName: String, weapon: String) { print("\ ...

随机推荐

  1. 怎样在一个项目里用logger在控制台打印信息

    第一步: 导入jar包,maven项目可以直接添加 <dependency><groupId>log4j</groupId><artifactId>lo ...

  2. 【nodejs】理想论坛帖子下载爬虫1.07 使用request模块后稳定多了

    在1.06版本时,访问网页采用的时http.request,但调用次数多以后就问题来了. 寻找别的方案时看到了https://cnodejs.org/topic/53142ef833dbcb076d0 ...

  3. IC卡制作常识概述

    ic卡主要有9种:    1.接触型IC卡:    2.非接触型IC卡:    3.串行传输型IC卡:    4.并行传输型IC卡:    5.存储型IC卡:    6.智能型IC卡:    7.超级 ...

  4. mysql存储过程,游标实例

    CREATE DEFINER=`root`@`%` PROCEDURE `vir`.`task_payment_byonlinedown`()begin declare _mobile varchar ...

  5. android中XRecyclerView控件的使用

    控件的地址:https://github.com/XRecyclerView/XRecyclerView XRecyclerView控件是一个加强版的RecyclerView,可以很方便的实现下拉刷新 ...

  6. iOS开发技巧 - Size Class与iOS 8多屏幕适配(一)

    0. 背景: 在iOS开发中,Auto Layout(自动布局)能解决大部分的屏幕适配问题. 但是当iPhone 6和iPhone 6 Plus发布以后, Auto Layout已经不能解决复杂的屏幕 ...

  7. Linux中最常用的JAVA_HOME配置

    一.配置 更改下面配置中的JAVA_HOME路径为你的路径. export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.144-0.b01.el7_ ...

  8. 分分钟带你玩转 Web Services【1】JAX-WS

    当大型需求被数个公司分割开来,各公司系统相互交换数据的问题就会接踵而来. 毕竟是多家不同的公司的产品,研发开发语言.采用技术框架基本上是百花齐放. 怎样让自家系统提供的服务具有跨平台.跨语言.跨各种防 ...

  9. 理解Android编译命令(转)

    一.引言 关于Android Build系统,这个话题很早就打算整理下,迟迟没有下笔,决定跟大家分享下.先看下面几条指令,相信编译过Android源码的人都再熟悉不过的. source setenv. ...

  10. java 图片与base64相互转化

      CreateTime--2017年12月4日17:38:44 Author:Marydon 需要导入: import java.io.FileInputStream; import java.io ...