Swift 中的可选链(Optional Chaining) 是一种安全、简洁地访问可选值中的属性、方法、下标的机制。如果可选值为 nil,整个链式调用会优雅地返回 nil,而不会崩溃。
一、核心语法
optionalValue?.property
optionalValue?.method()
optionalValue?[index]
使用
?代替.
如果optionalValue是nil→ 整个表达式返回nil
如果不是nil→ 继续访问
二、基础示例
class Person {
var name: String
var address: Address?
init(name: String) {
self.name = name
}
func greet() -> String {
return "Hi, I'm \(name)"
}
}
class Address {
var street: String
var city: String = "Beijing"
init(street: String) {
self.street = street
}
}
let person: Person? = Person(name: "Alice")
person?.address = Address(street: "中关村大街1号")
// 安全访问
let city = person?.address?.city
print(city) // Optional("Beijing")
// 如果 person 是 nil
let nobody: Person? = nil
let nobodyCity = nobody?.address?.city
print(nobodyCity) // nil(不会崩溃)
三、可选链支持的操作
| 操作 | 示例 |
|---|---|
| 属性访问 | person?.name |
| 方法调用 | person?.greet() |
| 下标访问 | array?.[0] |
| 链式组合 | person?.address?.street?.uppercased() |
四、完整链式调用示例
struct Book {
var title: String
var author: Author?
}
struct Author {
var name: String
var website: Website?
}
struct Website {
var url: URL
var isActive: Bool = true
}
// 构建数据
var book: Book? = Book(title: "Swift编程", author: Author(name: "张三", website: Website(url: URL(string: "https://example.com")!, isActive: true)))
// 可选链获取 URL 字符串
if let urlString = book?.author?.website?.url.absoluteString {
print("作者网站: \(urlString)")
} else {
print("无法获取网站")
}
// 输出: 作者网站: https://example.com
五、可选链 vs 传统解包
| 传统写法(繁琐) | 可选链(简洁) |
|---|
if let p = person {
if let a = p.address {
if let c = a.city {
print(c)
}
}
}
|
print(person?.address?.city)
|
六、可选链调用方法
class Player {
func play() -> String { "播放中..." }
func stop() { print("停止") }
}
let player: Player? = Player()
// 调用返回值的函数
let status = player?.play()
print(status) // Optional("播放中...")
// 调用无返回值的函数
player?.stop() // 如果 player 是 nil,什么都不发生
七、可选链 + 下标
let scores: [String: Int]? = ["Alice": 95, "Bob": 87]
let aliceScore = scores?["Alice"]
print(aliceScore) // Optional(95)
let carolScore = scores?["Carol"]
print(carolScore) // nil
八、可选链 + nil 合并运算符(常用组合)
let defaultCity = person?.address?.city ?? "未知城市"
print(defaultCity) // 如果 city 为 nil,返回 "未知城市"
九、可选链设置属性(需注意)
只有在可选值非 nil 时才赋值
person?.address?.city = "Shanghai" // 如果 person 或 address 是 nil,不会赋值
不会创建对象!仅在链条完整时生效
十、可选链与 if let 结合(高级)
if let city = person?.address?.city {
print("住在: \(city)")
} else {
print("地址信息不完整")
}
十一、可选链与 guard
func showWebsite(for book: Book?) {
guard let url = book?.author?.website?.url else {
print("无网站")
return
}
print("打开: \(url)")
}
十二、可选链在实际项目中的应用
1. JSON 解析
let json: [String: Any]? = ["user": ["name": "李四", "profile": ["avatar": "url"]]]
let avatar = json?["user"] as? [String: Any]
?["profile"] as? [String: Any]
?["avatar"] as? String
print(avatar) // Optional("url")
2. UI 控件安全访问
@IBOutlet weak var label: UILabel?
func updateUI() {
label?.text = viewModel?.title?.uppercased()
button?.isEnabled = viewModel?.isValid ?? false
}
十三、常见错误写法
| 错误写法 | 正确写法 |
|---|---|
person!.name(强制解包,易崩溃) | person?.name |
person?.address.city(编译错误) | person?.address?.city |
array[0]?.name(下标在前) | array?[0]?.name |
最佳实践总结
| 场景 | 推荐 |
|---|---|
| 访问嵌套可选属性 | ?. 可选链 |
| 调用可选对象方法 | object?.method() |
| 安全下标访问 | array?[index] |
| 默认值 | ?? |
| 提前返回 | guard let ... = x?.y?.z else { return } |
避免强制解包 ! | 除非 100% 确定非 nil |
常见面试题
- 可选链返回什么类型?
→ 总是Optional<T> - 可选链中间某一步是
nil,后续会执行吗?
→ 不会,整个表达式返回nil - 可选链可以修改属性吗?
→ 可以,但只有链条完整时才生效 - 以下代码输出什么?
var str: String?? = "Hello"
print(str??.count)
→ Optional(5)
- 可选链和
if let哪个性能更好?
→ 可选链更简洁,性能几乎无差别
小技巧
// 1. 可选链 + map(函数式)
let uppercased = person?.name.map { $0.uppercased() }
// 2. 可选链 + flatMap(压平嵌套可选)
let city = person?.address.flatMap { $0.city }
// 3. 安全调用协议方法
protocol Loggable {
func log()
}
extension Optional where Wrapped: Loggable {
func safeLog() {
self?.log()
}
}
optionalLogger?.safeLog()
高级话题(可继续提问)
- 可选链与 KeyPath
- 可选链在 SwiftUI 中的应用
- 自定义可选链操作符
- 编译器如何实现可选链(LLVM 层面)
- 可选链与
@autoclosure结合
需要完整 JSON 解析可选链实战、SwiftUI 绑定示例,或性能测试对比?欢迎继续提问!