本篇目标
理解可选类型的本质,掌握可选绑定、解包方式,学会使用 do-catch、throws、defer 处理错误,能编写健壮的 Swift 代码。
一、可选类型基础
可选类型表示一个值可能为空。
var name: String? = "Tom"
var age: Int? = nil语法:
String?等价于Optional<String>。nil表示没有值。非可选类型不能赋值为 nil。
var a: String = nil // 报错
var b: String? = nil // 正确为什么需要可选类型:
转换可能失败:
Int("abc")返回 nil。字典取值可能不存在。
网络请求可能失败。
API 返回值可能为空。
let str = "123"
let number = Int(str) // Optional(123)
let str2 = "abc"
let number2 = Int(str2) // nil二、强制解包
用 ! 强制解包,如果值为 nil 会崩溃。
var name: String? = "Tom"
print(name!) // Tom危险写法:
var name: String? = nil
print(name!) // 运行时崩溃建议:
尽量不用
!强制解包。只有确定不为 nil 时才用。
用可选绑定替代。
三、可选绑定
用 if let 或 guard let 安全解包。
if let:
var name: String? = "Tom"
if let unwrappedName = name {
print("Hello, \(unwrappedName)")
} else {
print("name 为 nil")
}简写(Swift 5.7+):
if let name {
print("Hello, \(name)")
}guard let:
func greet(name: String?) {
guard let name = name else {
print("name 为 nil")
return
}
print("Hello, \(name)")
}guard 与 if 的区别:
特性 | if let | guard let |
|---|---|---|
作用域 | 只在 if 块内 | 函数剩余部分都可用 |
必须退出 | 不需要 | else 中必须退出 |
适用场景 | 分支判断 | 参数校验 |
多个可选绑定:
let name: String? = "Tom"
let age: Int? = 25
if let name = name, let age = age {
print("\(name),\(age) 岁")
}带条件:
if let name = name, name.count > 2 {
print("名字长度超过 2")
}四、可选链
用 ?. 安全访问可选值的属性或方法。
class Person {
var name: String
var address: Address?
init(name: String) {
self.name = name
}
}
class Address {
var city: String
init(city: String) {
self.city = city
}
}
let person = Person(name: "Tom")
let city = person.address?.city // nil可选链的特点:
任意一层为 nil,整个表达式返回 nil。
返回值始终是可选类型。
可以链式调用。
person.address?.city?.count // Optional<Int>可选链调用方法:
person.address?.printInfo()五、nil 合并运算符
用 ?? 提供默认值。
let name: String? = nil
let displayName = name ?? "匿名"
print(displayName) // 匿名链式使用:
let a: String? = nil
let b: String? = nil
let result = a ?? b ?? "默认值"
print(result) // 默认值注意:
??左边是可选类型。右边是默认值,类型要与解包后一致。
六、隐式解包可选类型
用 ! 声明,使用时自动解包。
var name: String! = "Tom"
print(name) // 不需要 !使用场景:
IBOutlet 连接。
初始化后一定会赋值的属性。
class ViewController {
@IBOutlet var label: UILabel!
}风险:
值为 nil 时崩溃。
尽量少用,优先用可选绑定。
七、可选类型的比较
可选类型可以用 == 和 != 比较。
let a: Int? = 1
let b: Int? = 1
let c: Int? = nil
print(a == b) // true
print(a == c) // false
print(c == nil) // true注意:
不能直接用
>、<比较可选类型。需要先解包。
八、错误处理基础
Swift 用 throws 表示函数可能抛出错误。
enum FileError: Error {
case notFound
case noPermission
case invalidFormat
}
func readFile(path: String) throws -> String {
if path.isEmpty {
throw FileError.notFound
}
return "文件内容"
}调用 throws 函数:
do {
let content = try readFile(path: "test.txt")
print(content)
} catch {
print("读取失败:\(error)")
}九、do-catch
用 do-catch 捕获并处理错误。
do {
let content = try readFile(path: "")
print(content)
} catch FileError.notFound {
print("文件不存在")
} catch FileError.noPermission {
print("没有权限")
} catch {
print("其他错误:\(error)")
}catch 的用法:
可以针对特定错误类型捕获。
最后一个 catch 捕获所有错误。
error是隐式变量。
十、try 的三种形式
try:必须放在 do-catch 中,或函数声明为 throws。
do {
let content = try readFile(path: "test.txt")
} catch {
print(error)
}try?:失败返回 nil。
let content = try? readFile(path: "")
// content 是 String? 类型try!:失败时崩溃。
let content = try! readFile(path: "test.txt")对比:
形式 | 失败行为 | 返回类型 |
|---|---|---|
try | 抛出错误 | 正常类型 |
try? | 返回 nil | 可选类型 |
try! | 崩溃 | 正常类型 |
建议:
优先用 try + do-catch。
简单场景可用 try?。
尽量不用 try!。
十一、throws 函数与传播
函数可以声明为 throws,把错误向上抛。
func loadConfig() throws -> String {
let content = try readFile(path: "config.json")
return content
}调用方处理:
do {
let config = try loadConfig()
} catch {
print(error)
}throws 与 rethrows:
rethrows 表示只有闭包参数抛错时才抛错。
func perform(_ action: () throws -> Void) rethrows {
try action()
}十二、defer
defer 在离开当前作用域时执行,无论是否抛错。
func process() {
defer {
print("清理资源")
}
print("开始处理")
print("处理中")
}
process()
// 开始处理
// 处理中
// 清理资源执行顺序:
多个 defer 按逆序执行。
常用于文件关闭、锁释放、资源清理。
func openFile() {
defer { print("关闭文件") }
defer { print("解锁") }
print("打开文件")
}
// 打开文件
// 解锁
// 关闭文件十三、Result 类型
Result 是 Swift 提供的枚举,用于表示成功或失败。
enum Result<Success, Failure: Error> {
case success(Success)
case failure(Failure)
}使用示例:
func fetchData(completion: (Result<String, Error>) -> Void) {
let success = true
if success {
completion(.success("数据"))
} else {
completion(.failure(FileError.notFound))
}
}
fetchData { result in
switch result {
case .success(let data):
print("成功:\(data)")
case .failure(let error):
print("失败:\(error)")
}
}应用场景:
异步回调。
网络请求封装。
不想用 throws 的场景。
十四、自定义错误
错误类型遵循 Error 协议。
enum NetworkError: Error {
case noConnection
case timeout
case invalidResponse(code: Int)
}带关联值的错误:
enum ApiError: Error {
case httpError(statusCode: Int, message: String)
case parsingError(String)
case unknown
}
do {
throw ApiError.httpError(statusCode: 404, message: "Not Found")
} catch ApiError.httpError(let code, let message) {
print("HTTP \(code):\(message)")
} catch {
print(error)
}LocalizedError:
提供本地化描述。
enum LoginError: LocalizedError {
case invalidAccount
case wrongPassword
var errorDescription: String? {
switch self {
case .invalidAccount: return "账号无效"
case .wrongPassword: return "密码错误"
}
}
}十五、避坑指南
尽量不用强制解包:用可选绑定或 ?? 替代。
try! 慎用:出错会崩溃,只用于确定不会失败的场景。
guard 与 if let 选择:参数校验用 guard,分支逻辑用 if let。
defer 执行顺序:多个 defer 逆序执行。
可选链返回值:始终是可选类型,可能嵌套 Optional。
错误类型命名:用
-Error结尾,语义明确。错误传播:throws 函数要把错误往上抛,或本地处理。
十六、检查清单
[ ] 理解可选类型的本质
[ ] 能使用 if let、guard let 安全解包
[ ] 会使用可选链和 ??
[ ] 理解隐式解包可选类型
[ ] 能定义 throws 函数
[ ] 会使用 do-catch、try?、try!
[ ] 会使用 defer 清理资源
[ ] 能自定义错误类型
[ ] 了解 Result 类型