可选项,一般也叫可选类型,它允许将值设置为nil
在类型名称后加个问号?来定义一个可选项

  1. var name: String?
  2. name = nil
  3. // 默认是nil
  4. var age: Int?
  5. age = 10

强制解包

可选项是对其他类型的一层包装,可以将它理解为一个盲盒
如果为nil,那么它是个空盒子
如果不为nil,那么盒子里装的是:被包装类型的数据
image.png
如果要从可选项中取出被包装的数据(将盒子里装的东西取出来),需要使用感叹号!进行强制解包(拆盲盒)。

  1. var age: Int? = 10
  2. var ageInt: Int = age!
  3. ageInt += 10

如果对值为nil的可选项(空盒子)进行强制解包,将会产生运行时错误

  1. var age: Int?
  2. var ageInt: Int = age!

Fatal error: Unexpectedly found nil while unwrapping an Optional value

判断可选项是否包含值

  1. var num = Int("abc")
  2. if num != nil {
  3. print("num = \(num!)")
  4. }else {
  5. print("num = nil")
  6. }

可选项绑定(Optional Binding)

可以使用可选项绑定来判断可选项是否包含值
如果包含就自动解包,把值符给一个临时的常量(let)或者变量(var),并返回true,否则返回false

  1. if let number = Int("123") {
  2. print("number - \(number)")
  3. }else {
  4. print("number 转换成整数失败")
  5. }

上面代码是固定写法,Int(“123”)会尝试解包,并复制给number,如果成功了,则返回true,进入条件成功的判断,如果Int(“123”)是空,则返回false,进入条件失败的判断,number的作用域也仅限于条件成功的大括号内。

等价写法

  1. if let first = Int("1") {
  2. if let second = Int("2") {
  3. print("first = \(first), second = \(second)")
  4. }
  5. }
  6. if let first = Int("1"), let second = Int("2") {
  7. print("first = \(first), second = \(second)")
  8. }

这两个判断是等价的,可选项绑定判断需要使用逗号分隔

while循环中使用可选项绑定

  1. // 遍历数组,将遇到的整数都加起来,如果遇到负数或者非数字,停止遍历
  2. var strs = ["10", "20", "abc", "-10", "-20"]
  3. var sum = 0
  4. var index = 0
  5. while let num = Int(strs[index]), num > 0 {
  6. sum += num
  7. index += 1
  8. }
  9. print("sum = \(sum)")

空合并运算符 ??(Nil-Coalescing Operator)

a ?? b
a 是可选项
b 是可选项 或者 不是可选项
b 跟a的存储类型必须相同
如果a不为nil,就返回a
如果a为nil,就返回b
如果b不是可选项,返回a时会自动解包

  1. // 情况1:如果a不为nil,就返回a
  2. var a: Int? = 1
  3. var b: Int? = 2
  4. let c = a ?? b // c是Int?, Optional(1)
  5. // 情况2:如果a为nil,就返回b
  6. var a: Int? = nil
  7. var b: Int? = 2
  8. let c = a ?? b // c是Int?, Optional(2)
  9. var a: Int? = nil
  10. var b: Int? = nil
  11. let c = a ?? b // c是nil
  12. // 情况3:如果b不是可选项,返回a时会自动解包
  13. var a: Int? = 1
  14. var b: Int = 2
  15. let c = a ?? b // c是Int, 1

总结:如果b是可选项,则返回的是可选项,如果b是不可选项,则返回的是不可选项

多个 ??一起使用

  1. let a: Int? = 1
  2. let b: Int? = 2
  3. let c = a ?? b ?? 3 // c是Int,1

判断返回的是否是可选项,可以通过最右侧的数据类型。

??和if let 配合使用

  1. let a: Int? = nil
  2. let b: Int? = 2
  3. if let c = a ?? b {
  4. print(c)
  5. }

guard语句

  1. guard 条件 else {
  2. // do something
  3. 退出当前作用域
  4. // return、break、continue、throw error
  5. }

当guard语句的条件为false时,就回执行大括号里面的代码
当guard语句的条件为true时,就回跳过guard语句
guard语句特别适合来“提前退出”
当使用guard语句进行可选绑定时,绑定的常量、变量也能在外层作用域中使用

  1. func login(_ info: [String : String]) {
  2. guard let username = info["username"] else {
  3. print("请输入用户名")
  4. return
  5. }
  6. guard let password = info["password"] else {
  7. print("请输入密码")
  8. return
  9. }
  10. print("用户名 = \(username), 密码:\(password)")
  11. }

隐式解包(implicity Unwrapped Optional)

在某些情况下,可选项一旦被设定值后,就会一直拥有值
在某些情况下,可以去掉检查,也不必每次访问的时候都进行解包,因为它能确定每次访问的时候都有值
可以在类型后面加个感叹号!定义一个隐式解包的可选项

  1. let a: Int! = 10
  2. let b: Int = a
  3. print(b) // Int, 10

无论?还是!都代表可选项,只是!会在访问数据时自动解包
如果可选项时空值,会报错。
开发中尽量不要使用隐式解包和强制解包

字符串插值

可选项在字符串插值或者直接打印时,编译器会发出警告

  1. let a: Int? = 10
  2. print(a)
  3. print("a = \(a)")

至少有三种方法消除警告

  1. // 1、强制解包
  2. print("a = \(a!)") // a = 10
  3. // 2、打印描述字符串
  4. print("a = \(String(describing: a))") // a = Optional(10)
  5. // 3、使用??
  6. print("a = \(a ?? 0)") // a = 10

多重可选项

  1. // 包装了一个可选类型(一个盲盒)
  2. var a: Int? = 10
  3. // 包装了一个可选类型的可选类型(盲盒里面有一个盲盒)
  4. var b: Int?? = a
  5. // 系统会自动包装成和b一样的结构
  6. var c: Int?? = 20

a如下图所示:
image.png
b如下图所示:
image.png
c和b的结构一致

  1. var a: Int? = 10
  2. var b: Int?? = a
  3. // 如果直接赋值为空,则内部为空值
  4. var c: Int?? = nil

c的结果如下图所示:
image.png