Swift 模式:解构与匹配的安全之道

什么是"模式"

在 Swift 中,模式(Pattern) 描述了一个值的结构,而不是具体的某个值。

借助模式,我们可以:

  1. 检查某个值是否符合该结构(匹配)
  2. 把符合结构的值拆解出来,绑定到新的变量 / 常量(绑定)

模式出现的常见场景:

  • 变量 / 常量声明:let (x, y) = point
  • switchcase 标签
  • if / guard / while / for-in 的条件
  • catch 异常捕获

Swift 将模式分为两大类:

类别 特点 典型模式
必然匹配型 只要类型对,就一定成功,不会运行时失败 通配符、标识符、元组、值绑定
可能失败型 运行时才知是否匹配,可能走 default 分支 枚举 case、可选、类型转换、表达式

必然匹配型模式

通配符模式(Wildcard Pattern)

写法:一个下划线 _

作用:匹配并丢弃任意值,常用于"我不关心"的场景。

swift 复制代码
// 只想循环 3 次,不需要索引
for _ in 1...3 {
    print("拍一张")
}

标识符模式(Identifier Pattern)

写法:一个变量 / 常量名

作用:匹配任意值,并把它绑定到该名字。

swift 复制代码
let someValue = 42   // someValue 就是标识符模式

注意:当标识符出现在赋值左侧时,它隐含地被包装在一个"值绑定模式"里。

值绑定模式(Value-Binding Pattern)

写法:以 let / var 开头,可"分发"到子模式

作用:把匹配到的值绑定到新的变量 / 常量。

swift 复制代码
let point = (3, 4)
switch point {
case let (x, y):          // let 分发到 x、y
    print("坐标:(\(x), \(y))")
default:
    break
}
// 等价于 case (let x, let y):

元组模式(Tuple Pattern)

写法:圆括号包裹的"零个或多个子模式"列表

作用:按结构匹配元组;可嵌套;可带类型标注做类型约束。

swift 复制代码
// 1. 基本拆解
let (a, b) = (1, 2)

// 2. 类型约束:只能匹配 (Int, Int)
let (x, y): (Int, Int) = (3, 4)

// 3. 嵌套 + 通配符
let rgb = (r: 255, g: 60, b: 90)
switch rgb {
case (let r, _, _):      // 只要红色分量
    print("红色值 = \(r)")
default:
    break
}

⚠️ 限制:当元组模式用于 for-in / 变量声明时,子模式只能是:通配符、标识符、可选、元组,不能是表达式。

因此下面代码非法:

swift 复制代码
let (x, 0) = (5, 0)   // 0 是表达式模式,编译报错

可能失败型模式

枚举 case 模式(Enumeration Case Pattern)

写法:.caseNameEnumType.caseName,有关联值时再跟元组

作用:匹配具体某个枚举成员;可一并提取关联值。

swift 复制代码
enum NetworkResponse {
    case success(body: String)
    case failure(code: Int, message: String)
}

let res: NetworkResponse = .success(body: "OK")

switch res {
case .success(let body):
    print("成功:\(body)")
case .failure(let code, let msg):
    print("错误 \(code):\(msg)")
}

小技巧:Swift 把 Optional 也当成枚举,因此 .some / .none 可与其自定义枚举混用:

swift 复制代码
enum NetworkResponse {
    case success(body: String)
    case failure(code: Int, message: String)
}
let maybe: NetworkResponse? = .success(body: "Maybe")
switch maybe {
case .success(let body):   // 省略 .some 写法
    print(body)
case .failure(_, _):
    print("失败")
case .none:
    print("无值")
}

可选模式(Optional Pattern)

写法:在标识符后加 ?

本质:语法糖,等价于 .some(wrapped) 的枚举 case 模式

场景:快速解包数组 / 字典里的可选值

swift 复制代码
let nums: [Int?] = [1, nil, 3, nil, 5]
for case let num? in nums {   // 只遍历非 nil
    print(num)                // 输出 1 3 5
}

类型转换模式(Type-Casting Pattern)

模式 出现位置 作用
is Type switchcase 判断运行时类型是否匹配,不绑定新名
pattern as Type switch / if / guard ... 判断并强转,匹配成功则绑定到新模式
swift 复制代码
let mixed: [Any] = [1, "hi", 3.14, 40]

for element in mixed {
    switch element {
    case is String:
        print("遇到字符串")
    case let i as Int where i > 30:
        print("大于 30 的整数:\(i)")
    default:
        break
    }
}

表达式模式(Expression Pattern)

写法:任意表达式

原理:Swift 使用标准库里的 ~= 运算符做匹配;默认实现用 ==,但可重载实现自定义规则。

swift 复制代码
let count = 7

// 默认行为:相等比较
switch count {
case 0:
    print("零")
default:
    print("非零")
}

// 自定义 ~=,让字符串也能匹配整数
func ~= (pattern: String, value: Int) -> Bool {
    return pattern == "\(value)"
}

switch count {
case "7":            // 自定义后返回 true
    print("幸运数字 7")
default:
    break
}

利用重载,可实现"范围匹配"、"正则匹配"、"自定义业务规则匹配"等高级玩法。

一个综合示例:把模式用活

swift 复制代码
// 1. 定义模型
enum Command {
    case move(x: Int, y: Int)
    case pen(up: Bool)
    case repeatTimes(times: Int, [Command])   // 嵌套命令
}

// 2. 解析脚本
let scripts: [Command?] = [
    .move(x: 10, y: 20),
    nil,
    .repeatTimes(times: 2, [.pen(up: true), .move(x: 5, y: 0)])
]

// 3. 深度遍历,模式一网打尽
func walk(_ cmds: [Command?]) {
    for case let cmd? in cmds {          // 可选模式去 nil
        switch cmd {
            // 3.1 关联值直接拆出
        case .move(let x, let y):
            print("移动 to (\(x), \(y))")
            // 3.2 布尔判断
        case .pen(up: true):
            print("抬笔")
        case .pen(up: false):
            print("落笔")
            // 3.3 嵌套递归
        case .repeatTimes(times: let n, let sub):
            print("开始重复 \(n) 次")
            for _ in 0..<n { walk(sub) }   // 递归
        }
    }
}

walk(scripts)

输出: 移动 to (10, 20) 开始重复 2 次 抬笔 移动 to (5, 0) 抬笔 移动 to (5, 0)

总结与扩展思考

  1. 模式是 Swift 的"静态检查 + 运行时匹配"双保险

    编译期保证结构合法;运行期再决定走哪条分支,既安全又灵活。

  2. 用好"必然匹配"能少写样板代码

    例如 let (x, y) = point 一行完成解构;for case let num? 一行完成解包过滤。

  3. "可能失败"模式让业务语义显性化

    把"枚举 case、类型判断、可选解包"统统搬进 switch,代码即文档,可读性远高于一堆 if else

  4. 重载 ~= 是隐藏大杀器

    正则、范围、区间、甚至"数据库记录是否存在"都可以抽象成模式,配合 switch 写出声明式代码。

  5. 模式不仅存在于语法,更存在于设计

    把"什么结构合法"提前定义成枚举 / 元组 / 协议,再用模式去匹配,天然契合"非法状态无法表示"的 Swift 哲学。

相关推荐
东坡肘子5 小时前
Swift 官方发布 Android SDK | 肘子的 Swift 周报 #0108
android·swiftui·swift
YGGP2 天前
【Swift】LeetCode 53. 最大子数组和
swift
2501_916008892 天前
用多工具组合把 iOS 混淆做成可复用的工程能力(iOS混淆|IPA加固|无源码混淆|Ipa Guard|Swift Shield)
android·开发语言·ios·小程序·uni-app·iphone·swift
胎粉仔2 天前
Swift 初阶 —— inout 参数 & 数据独占问题
开发语言·ios·swift·1024程序员节
HarderCoder2 天前
Swift 下标(Subscripts)详解:从基础到进阶的完整指南
swift
YGGP2 天前
【Swift】LeetCode 189. 轮转数组
swift
JZXStudio2 天前
5.A.swift 使用指南
框架·swift·app开发
非专业程序员Ping3 天前
HarfBuzz概览
android·ios·swift·font
Daniel_Coder3 天前
iOS Widget 开发-8:手动刷新 Widget:WidgetCenter 与刷新控制实践
ios·swift·widget·1024程序员节·widgetcenter