元组:轻量级"匿名结构体"
- 快速组装
 
            
            
              swift
              
              
            
          
          // 1. 不命名元素
let http404 = (404, "Not Found")
print(http404.0)   // 404
// 2. 命名元素(推荐,可读性≈struct)
let http200 = (code: 200, description: "OK")
print(http200.code)
        - 解构赋值
 
            
            
              swift
              
              
            
          
          let (statusCode, statusText) = http404
// 只想要其中一个
let (code, _) = http404
        - 函数多返回值------官方最推崇场景
 
            
            
              swift
              
              
            
          
          /// 返回"商"和"余"
func divide(_ a: Int, by b: Int) -> (quotient: Int, remainder: Int)? {
    guard b != 0 else { return nil }   // 除 0 返回 nil
    return (a / b, a % b)
}
if let result = divide(10, by: 3) {
    print("商:\(result.quotient),余:\(result.remainder)")
}
        对比单独建 struct:
- 一次性接口,无需额外类型污染命名空间;
 - 超过 3 个字段或需要方法 → 果断建 struct/class。
 
- 与 switch 搭配
 
            
            
              swift
              
              
            
          
          let point = (0, 0)
switch point {
case (0, 0):
    print("原点")
case (_, 0):
    print("在 x 轴")
case (0, _):
    print("在 y 轴")
default:
    print("象限内")
}
        错误处理:比 Optional 更丰富的失败信息
- 错误类型必须遵 Error 协议
 
            
            
              swift
              
              
            
          
          enum SandwichError: Error, LocalizedError {
    case outOfCleanDishes
    case missingIngredients([String])
    
    var errorDescription: String? {
        switch self {
        case .outOfCleanDishes:
            return "没有干净的盘子"
        case .missingIngredients(let list):
            return "缺少食材:\(list.joined(separator: ", "))"
        }
    }
}
        - 函数标记 throws
 
            
            
              swift
              
              
            
          
          class Sandwich {}
func makeSandwich() throws -> Sandwich {
    let dishes = arc4random()
    let pantry = Set<String>(["Bread", "Any", "has"])
    guard dishes > 0 else { throw SandwichError.outOfCleanDishes }
    guard pantry.contains("Bread") && pantry.contains("Ham") else {
        throw SandwichError.missingIngredients(["Bread", "Ham"])
    }
    return Sandwich()
}
        - 调用方三种写法
 
| 写法 | 场景 | 备注 | 
|---|---|---|
try? | 
失败就变 nil | 丢弃错误细节 | 
try! | 
100% 不会错 | 崩 | 
do-catch | 
要细分错误 | 最常用 | 
            
            
              swift
              
              
            
          
          // 1) try? ------ 快速转 Optional
let sandwich = try? makeSandwich()   // Sandwich?
// 2) do-catch ------ 细粒度处理
func eat(_ wich: Sandwich) {
    
}
func washDishes() {
    
}
func buyGroceries(_ list: [String]) {
    
}
do {
    let sandwich = try makeSandwich()
    eat(sandwich)
} catch SandwichError.outOfCleanDishes {
    washDishes()
} catch SandwichError.missingIngredients(let list) {
    buyGroceries(list)
} catch {
    // 兜底,必须写,否则新增错误 case 会漏
    print("未知错误:\(error)")
}
        - rethrows------高阶函数也能"转发"错误
 
            
            
              swift
              
              
            
          
          func retry(_ times: Int, _ body: () throws -> Void) rethrows {
    for _ in 0..<times {
        do {
            try body()
        } catch {
            throw error
        }
    }
}
        - 错误传递链------跨层穿透
 
            
            
              swift
              
              
            
          
          // DAO → Service → UI
func loadUser() throws -> User   // DAO
func presentProfile() throws     // UI
// 任意一层不 catch,编译器自动加 throw,无需手工 return
        Assertions vs Preconditions------何时让程序"死"
| 方法 | 生效构建 | 失败行为 | 用途 | 性能影响 | 
|---|---|---|---|---|
assert | 
Debug | 终止 + 日志 | 开发期抓 Bug | Release 构建中被移除 | 
precondition | 
Debug + Release | 终止 + 日志 | 生产期防脏数据 | Release 构建中仍会检查 | 
示例:
            
            
              swift
              
              
            
          
          let depth = arc4random() % 10
// 开发期:算法入参必须 ≥ 0
assert(depth >= 0, "递归深度不能为负")
let number = "1252412342390847"
// 生产期:银行卡号长度必须 16-19
precondition(number.count >= 16 && number.count <= 19,
             "卡号非法,立即终止流程")
        进阶:
assertionFailure/preconditionFailure------ 条件已隐含,直接报失败- 单元测试可用 
XCTAssert家族,与assert不冲突 
综合实战
目标:写个 CLI 小工具,扫描指定目录下的 .swift 文件,统计
① 代码行数 ② 空行数 ③ 注释行数,结果通过元组返回;
若目录不存在则抛错;用断言保证"行数 ≥ 0"这一不变式。
            
            
              swift
              
              
            
          
          #!/usr/bin/env swift
import Foundation
enum CLIError: Error {
    case directoryNotFound
}
typealias Stat = (code: Int, blank: Int, comment: Int)   // 类型别名
/// 统计单个文件
func analyze(_ path: String) throws -> Stat {
    let content = try String(contentsOfFile: path)
    var code = 0, blank = 0, comment = 0
    for line in content.split(separator: "\n", omittingEmptySubsequences: false) {
        let trim = line.trimmingCharacters(in: .whitespaces)
        if trim.isEmpty {
            blank += 1
        } else if trim.hasPrefix("//") {
            comment += 1
        } else {
            code += 1
        }
    }
    assert(code >= 0 && blank >= 0 && comment >= 0, "行数不能为负")
    return (code, blank, comment)
}
/// 递归目录
func analyzeDirectory(_ path: String) throws -> Stat {
    guard FileManager.default.fileExists(atPath: path) else {
        throw CLIError.directoryNotFound
    }
    var total: Stat = (0, 0, 0)
    let files = FileManager.default.enumerator(atPath: path)!
    for case let file as String in files {
        guard file.hasSuffix(".swift") else { continue }
        let sub = try analyze("\(path)/\(file)")
        total = (total.code + sub.code,
                 total.blank + sub.blank,
                 total.comment + sub.comment)
    }
    return total
}
// CLI 入口
do {
    let result = try analyzeDirectory(FileManager.default.currentDirectoryPath)
    print("Swift 文件统计")
    print("代码行:\(result.code)")
    print("空行:\(result.blank)")
    print("注释行:\(result.comment)")
} catch CLIError.directoryNotFound {
    print("错误:目录不存在")
} catch {
    print("未知错误:\(error)")
}
        运行:
            
            
              bash
              
              
            
          
          ❯ swift stat.swift
Swift 文件统计
代码行:544157
空行:105382
注释行:101237
        再谈"什么时候用哪个特性"------一张思维导图
            
            
              vbnet
              
              
            
          
          需要表达"没有值"
├─ 仅关心"有没有" → Optional
├─ 关心"为什么失败" → Error
├─ 开发期断言 → assert
└─ 生产期哨兵 → precondition
需要多值返回
├─ 临时一次性 → Tuple
├─ 需要方法/协议 → Struct/Class
└─ 需要引用语义 → Class
需要类型别名
├─ 底层数据不变,语义更清晰 → typealias
└─ 跨平台兼容 → typealias + 条件编译 #if arch()