首页 / Swift 编程语言教程 / nil 合并运算符 ??

Swift 编程语言教程

nil 合并运算符 ??

本教程共 93 篇 · 第 43 篇 · 更新于 2026-08-08 · 约 7 分钟阅读

SwiftSwift 编程语言教程nil合并运算符nil-coalescing可选类型默认值可选链短路

本节目标:掌握 ?? 运算符,在可选值为 nil 时给出一个默认值,从而把可选类型安全地转成普通值。

前面学了可选链,能安全地从可选值里取值,但取出来的结果还是可选类型。很多时候你想要的只是一个”实在的值”——有就用里面的,没有就用我给的默认值。运算符 ??(nil 合并运算符)就是干这个的。

1-1 ?? 的基本写法

a ?? b 的含义是:如果 a 有值,就用 a 里的值;如果 anil,就用 ba 必须是可选类型,b 的类型要和 a 解包后的类型一致(或兼容)。

let defaultColor = "red"
var userColor: String? = nil

let color = userColor ?? defaultColor   // color 是 "red"

userColornil,所以 ?? 取后面的默认值 "red"color 因此是一个普通 String。如果 userColor 有值,color 就会用那个值。

1-2 它等价于什么

?? 只是一种简写。上面的代码用 if let 写出来是:

let color: String
if let userColor = userColor {
    color = userColor
} else {
    color = defaultColor
}

显然 ?? 一行顶上五行的效果,而且结果同样是普通 String,可以直接拿去用,不用再解包。这就是为什么 ?? 如此讨人喜欢:它既是安全的(不会崩),又产出非可选值。

Note

?? 具有短路特性:只有当左边是 nil 时,右边才会被求值。如果左边有值,右边那表达式根本不会执行。所以把”计算代价大”的默认值放在右边是安全的,有值时它不会白算。

1-3 与可选链搭配

?? 和可选链是天生一对。可选链取出来的是可选,再用 ?? 兜底,整条表达式既安全又直接得到普通值:

let roomCount = john.residence?.numberOfRooms ?? 0

john.residence?.numberOfRoomsInt?:如果 residence 有值就取房间数,否则为 nil?? 0 保证最终 roomCount 一定是普通 Int,没有房间时算 0。这一行比手写 if let 清爽太多。

1-4 链式使用 ??

你可以把多个 ?? 连起来,依次提供候选默认值:最左边的可选为 nil 就试下一个,再 nil 再下一个,直到拿到一个非 nil 的值。

let preferred: String? = nil
let fallback: String? = nil
let final = preferred ?? fallback ?? "默认名称"
// final 为 "默认名称"

只有前面所有可选都为 nil,才会落到最后的字面量。注意每往后一级,左操作数必须是可选类型,这样链式才有意义。

Tip

默认值那一侧本身也可以是可选,但那样 ?? 的结果还是可选。要让结果变成普通值,最后一个兜底项一定是非可选项,比如字面量 "默认" 或常量 0

1-5 短路特性与组合用法

理解 ?? 的”短路”,能帮你写出更地道的代码。短路的意思是:只有当左边那个可选真的是 nil 时,右边才会被求值。这带来一个实用推论——把”计算代价高”的默认值放在右边是安全的:有值时它根本不会执行,不会白白浪费性能。比如 userSettings ?? loadDefaultSettings(),只要 userSettings 有值,loadDefaultSettings() 这个函数调用压根不会发生。反过来,如果你把高代价表达式直接写在左边那种必然执行的位置,那才是浪费。

?? 和可选链组合,是日常最顺手的模式之一。可选链取出的是可选,再用 ?? 兜底,一步到位拿到普通值。例如读取一个深层配置:config?.database?.timeout ?? 30,链条上任意一环为 nil,最终都回落到默认 30,而 timeout 直接是普通 Int,后面可以放心用于计算。这种写法把”可能缺失”和”给默认值”压缩成一行,既安全又清晰。

链式 ?? 也值得掌握:多个候选依次尝试,preferred ?? fallback ?? "默认",谁先非 nil 用谁。这在”用户设置 → 系统默认 → 硬编码兜底”这类三级回退里很常见。注意一点:要让最终结果落在普通类型上,链上最后一个兜底项必须是非可选项(字面量或普通值);如果最后一项还是可选,?? 的结果就仍然是可选,等于没完全”解”出来。

还有一个容易忽略的细节:?? 的优先级较低,表达式复杂时建议给左右两侧适当加括号,避免和可选链、三元运算混在一起时产生歧义。可读性优先,别为了省括号牺牲清晰。

很多初学者会问,??if let 该选哪个?答案是看你需不需要”默认值”这个语义。?? 最适合”没值就用某个默认”;如果你在没值时想做别的事(比如报错、跳转),那 if let/guard let 更合适。?? 不是 if let 的替代品,而是它的一个特化捷径。理解各自的定位,代码才不会被一种写法硬套所有场景。

1-6 易错点速记

?? 的短路特性值得记住:左边有值,右边根本不执行。所以把”昂贵的计算”放在右边是安全的,有值时它不会白算。

?? 常和可选链搭档:config?.db?.timeout ?? 30,链条上任意一环 nil 都回落到默认 30,结果直接是普通类型。链式 ?? 做多级回退很好用,但最后一项必须是非可选项,否则结果仍是可选,等于没完全解出来。

优先级偏低是另一个坑:?? 和可选链、三元运算符混用时,适当加括号避免歧义。可读性优先。最后分清 ??if let 的分工:?? 专管”没值就用默认”;没值时你想做别的事(报错、跳转),用 if let/guard let 更合适。它俩不是替代关系。

一个常见误用是把 ?? 当成”永远安全”而到处套,结果默认值掩盖了本该暴露的问题。如果”没值”本身是异常情况、需要调用方知道,那就该用可选绑定或错误,而不是默默用默认值盖过去。

一句话总结:?? 是”有就用、无则默认”的收尾利器,和可选链配合能把”可能缺失”干净地变成”一定存在”。

1-7 小结

?? 是处理可选类型最顺手的收尾工具:有值用值、无值用默认,结果直接是普通类型,省去再解包的麻烦。它和可选链配合尤其常见,一行代码就能安全地把”可能缺失”变成”一定存在”。下一章我们离开可选类型,进入另一套安全机制——错误处理。