从这篇文章,你将学习到如何使用 Swift 可选类型,如果稍微有点其他语言的编程经验,都会大概使用过对空值的表示方法,nil、null 和 none 傻傻分不清楚,让我们开始吧。

Swift Optionals

什么是可选类型

Swift 中为解决对空值的表示采用了可选类型 Optional,表明所定义的变量要么有值,要么没有值,还要知道可选类型不仅可以用在引用类型,还可以用在像 Int 这样的值类型。

var choice: Int? // 定义了一个可选 Int 类型的变量
choice = 5 // 变量可以有值为 5
choice = nil // 变量可以没有任何值为空
choice = 0 // 0 也是有值,不是空

展开可选类型

为什么要展开可选类型,先看看下面这个例子:

var name: String? = "Jack"
var greeting = "Hello, " + name

在 greeting 这一行会报如下错误:

error: value of optional type 'String?' not unwrapped; did you mean to use '!' or '?'?

Swift 是门讲究少出错的语言,在使用可选类型进行运算前,要通过下面几种方式展开可选类型保证可选类型有值,再进行其他运算。

强制展开

你非常确定,可选类型在运算时,确实有值,你可以采用下面这种方式,但是,运行时如果没有值,程序就会崩溃。

var name: String? = "Jack"
var greeting = "Hello, " + name!

if let 绑定

如果 name 有值,程序会走 if 这一段,并将 name 赋值给 n;如果 name 没有值,会走 else 这一段。

var name: String? = "Jack"
if let n = name {
  print("Hello, " + n)
} else {
  print("Hello, Stranger")
}

有多个可选类型的情况:

var age: Int? = 5
var name: String? = "Jack"
if let n = name, let a = age {
  print("Hello \(n). You are \(a) years old.")
} else {
  print("Hello stranger. How old are you?")
}

guard let

func greeting(name: String?) {
  guard let n = name else {
    print("Hello, Stranger")
    return
  }
  print("Hello, " + n)
}

func flatMap(_:)

如果可选类型不为 nil,会将展开后值作为参数传给 transform 闭包,闭包会返回转化后的 可选类型

func flatMap<U>(_ transform: (Wrapped) throws -> U?) rethrows -> U?

let possibleNumber: Int? = Int("42")
let nonOverflowingSquare = possibleNumber.flatMap { x -> Int? in
    let (result, overflowed) = Int.multiplyWithOverflow(x, x)
    return overflowed ? nil : result
}
print(nonOverflowingSquare)
// Prints "Optional(1746)"

func map(_:)

如果可选类型不为 nil,会将展开后值作为参数传给 transform 闭包,闭包会返回转化后的 非可选类型

func map<U>(_ transform: (Wrapped) throws -> U) rethrows -> U?

let possibleNumber: Int? = Int("42")
let possibleSquare = possibleNumber.map { $0 * $0 }
print(possibleSquare)
// Prints "Optional(1746)"
 
let noNumber: Int? = nil
let noSquare = noNumber.map { $0 * $0 }
print(noSquare)
// Prints "nil"

隐式展开可选类型

有时候,一个变量在初始化时,可能为 nil,当被赋值后保证不会为 nil,这种情况下就应该使用隐式展开可选类型来简化调用时的代码。好绕口。

class LeftViewController: NSViewController {

  @IBOutlet weak var searchField: NSSearchField!

}

上面的示例中,searchFieldLeftViewController 调用 init 方法初始化时还是 nil,但是 LeftViewControllerXIB 中加载了相关视图过后,就不再为 nil,在后面逻辑代码处理中可以放心使用类似 searchField.doSomething() 这样的代码。

空合并运算符

在操作可选类型时候,一种简便的提供默认值的写法。

let defaultWeight = 60
var inputWeight: Int?

var weight = inputWeight ?? defaultWeight
// inputWeight 没有值,weight 结果是 60

inputWeight = 55
weight = inputWeight ?? defaultWeight
// inputWeight 有值,weight 结果是 55

可选链

可选链是一种在可选类型上访问属性、调用方法和访问下标的过程,如果可选类型有值,那么调用成功,如果可选类型没有值,整个过程将返回 nil,多个调用过程可以链接起来,整个链中如果任意一个可选类型没有值,则整个链返回 nil。

class Person {
  var residence: Residence?
}

class Residence {
  var numberOfRooms = 1
}

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.")
}