如何愉快地处理 Optional

比较好的几种处理 Optional 的方法。

1.「 ?? 」 操作符

在使用 ?? 之前,先来复习下三目运算符:

1
2
let max = a > b ? a : b
//如果 a > b 为真,返回 a, 为假返回 b。

再来看下 ?? 的用法

1
2
3
4
5
let i: Int? = 100
let x = i ?? 0 // x = 100

let y: Int? = nil
let z = y ?? 0 // y = 0

?? 与三目运算类似,如果一个 optional 有值,使用值。如果没值,使用 ?? 后面的值。

如果按住 ⌥ Optoian 点击 xz。可以看到它们的类型推导为 let x: Int let z: Int,并不是一个可选值

利用 ?? 操作符,可以起到给一个 optional 属性设置一个默认值的作用。 在后续使用中不需要考虑解包的问题。

2. Optional Binding 可选绑定

2.1 if let

来看一个小🌰:

1
2
3
4
5
6
7
8
9
10
func contact(name: String?, phone: String?) {
if let name = name, let phone = phone {
// 只有当 name 和 phone 一定有值,
才会进入该分支
let contact = name + " " + phone // 可以直接参与计算
print(contact)
} else {
print("name or phone nil")
}
}

例子中的函数,有两个参数并且都是可选的。那么在函数内部就要考虑到参数的解包问题。使用 if let 可选绑定成功后,name 和 age 都一定有值,可直接使用。它们的类型推导都是非可选值。

可以看到,我在使用 if let name = name 的时候,使用了同名的变量来接收值,在后续使用都是非空值。可以避免想变量名的烦恼…

在整个函数实现内部,没有出现一个 ? 或者 ! 。是不是看到和清爽?(并且更安全)

2.2 guard let

guard let 刚好与 if let 相反

再来一个🌰:

1
2
3
4
5
6
7
8
9
10
11
func contact(name: String?, phone: String?) {
guard let name = name, let phone = phone else {
// 当 name 或者 phone 为 nil,才会进入该分支
print("name or phone nil")
return // 在分支的最后,需要有 return 或者 break ...
}

// 如果没有进入 guard 分支,则 name 和 phone 都有值,可直接参与计算
let contact = name + " " + phone
print(contact)
}

guard let 在日常开发使用的更多一些,我们可以在 guard 分支里做一些错误处理,然后返回。

如果没有进入 guard 分支,那么后面那些变量的都是有值的。可以直接使用

同样的还有 if varguard var

3. Optional Map

如果有 let i: Int? ,i 是一个整型可选值,我想对它 *2 并且返回一个新的值。如果 i 有值的话就 *2 然后返回,如果没值则返回 nil。使用可选绑定可以这么写:

1
2
3
4
5
6
var x: Int?
if let i = i {
x = i * 2
} else {
x = nil
}

但有个更简洁的方法是使用 map

1
2
let i: Int? = 100
let x = i.map { $0 * 2 } // --> let x: Int?; x = 200

使用 map 可以方便地操作一个 optional,而不需要进行手动的解包工作