Swift Switch 语句的特性与模式匹配(9)

Swift Switch 语句的特性与模式匹配:源码级别分析

一、Swift Switch 概述

1.1 基本概念

Swift 中的 switch 语句是一种强大的控制流结构,用于基于不同条件执行不同的代码分支。与 C 和 Objective-C 中的 switch 语句相比,Swift 的 switch 语句更加安全、灵活且表达力更强。

在 C 和 Objective-C 中,switch 语句通常只能对整数类型进行比较,并且需要显式使用 break 语句来避免执行穿透(fallthrough)。而 Swift 的 switch 语句可以匹配多种类型的值,包括但不限于整数、浮点数、字符串、元组、枚举值、范围匹配以及类型模式匹配等,并且默认情况下不会发生执行穿透,从而减少了因忘记添加 break 而导致的错误。

1.2 语法结构

Swift switch 语句的基本语法结构如下:

switch valueToMatch {
case pattern1:
    // 代码块1
case pattern2, pattern3:
    // 代码块2
default:
    // 默认代码块
}

其中:

  • valueToMatch 是要进行匹配的值。
  • pattern1pattern2 等是匹配模式,可以是单个模式或多个模式的组合。
  • 每个 case 分支后面可以跟随一个或多个语句。
  • default 分支是可选的,但如果所有可能的情况没有被 case 分支覆盖,则必须提供 default 分支。

1.3 核心特性

Swift switch 语句的核心特性包括:

  1. 模式匹配的多样性:可以匹配多种类型和模式,包括值绑定、范围匹配、类型转换等。

  2. 安全性:必须覆盖所有可能的情况,否则会导致编译错误,除非提供了 default 分支。

  3. 不允许隐式贯穿:默认情况下,执行完一个 case 分支后不会继续执行下一个 case 分支,不需要使用 break 语句。

  4. 复合匹配:单个 case 分支可以匹配多个模式,用逗号分隔。

  5. 值绑定:可以在模式中绑定常量或变量,以便在 case 分支的代码块中使用。

  6. where 子句:可以使用 where 子句进一步过滤匹配条件。

下面通过一个简单的例子来展示 Swift switch 语句的基本用法:

let number = 5

switch number {
case 0:
    print("数字是0")
case 1...5:
    print("数字在1到5之间")
case 6, 7, 8:
    print("数字是6、7或8")
case let x where x > 8:
    print("数字大于8,值为: \(x)")
default:
    print("数字是负数")
}

在这个例子中,switch 语句对变量 number 进行匹配:

  • 第一个 case 分支匹配值为 0 的情况。
  • 第二个 case 分支使用范围匹配,匹配值在 1 到 5 之间的情况。
  • 第三个 case 分支使用复合匹配,匹配值为 6、7 或 8 的情况。
  • 第四个 case 分支使用值绑定和 where 子句,匹配值大于 8 的情况,并将匹配的值绑定到常量 x 中。
  • default 分支处理所有其他情况,即 number 为负数的情况。

二、Swift 模式匹配基础

2.1 模式的定义与分类

在 Swift 中,模式是用于匹配值的一种特殊语法结构。模式可以用于多种场景,如变量声明、switch 语句、for-in 循环等。根据 Swift 官方文档,模式可以分为以下几类:

  1. 通配符模式(Wildcard Pattern):使用下划线(_)表示,匹配并忽略任何值。

  2. 标识符模式(Identifier Pattern):用于匹配并将值绑定到变量或常量。

  3. 值绑定模式(Value-Binding Pattern):用于在匹配过程中将值绑定到变量或常量。

  4. 元组模式(Tuple Pattern):用于匹配元组类型的值,并可以嵌套其他模式。

  5. 枚举用例模式(Enumeration Case Pattern):用于匹配枚举类型的特定用例。

  6. 可选模式(Optional Pattern):用于匹配可选类型的值。

  7. 类型转换模式(Type-Casting Pattern):用于检查值的类型并进行类型转换。

  8. 表达式模式(Expression Pattern):用于通过比较操作符进行值的匹配。

2.2 通配符模式

通配符模式使用下划线(_)表示,用于匹配任何值并忽略它。通配符模式通常用于不需要关心具体匹配值的场景,例如:

let someValue = 42

switch someValue {
case _:
    print("匹配到了某个值")
}

在这个例子中,switch 语句的 case 分支使用通配符模式,因此无论 someValue 的值是什么,都会执行对应的代码块。

通配符模式在闭包参数中也经常使用,当闭包参数不需要被使用时,可以用下划线替代:

let numbers = [1, 2, 3, 4, 5]
numbers.forEach { _ in
    print("处理一个数字")
}

2.3 标识符模式

标识符模式用于匹配并将值绑定到变量或常量。在变量或常量声明中,等号左边的部分就是一个标识符模式。例如:

let x = 10
var y = "hello"

在这些例子中,xy 都是标识符模式,它们分别匹配并绑定了右边的值。

标识符模式也可以在 switch 语句的 case 分支中使用,例如:

let someValue: Any = 42

switch someValue {
case let number as Int:
    print("匹配到整数: \(number)")
case let text as String:
    print("匹配到字符串: \(text)")
default:
    print("匹配到其他类型")
}

在这个例子中,let number as Intlet text as String 都是标识符模式,它们分别将匹配的值绑定到常量 numbertext 中。

2.4 值绑定模式

值绑定模式是标识符模式的一种扩展,它允许在匹配过程中将值绑定到变量或常量。值绑定模式通常用于元组模式、枚举用例模式等复杂模式中。

例如,在元组模式中使用值绑定:

let point = (3, 5)

switch point {
case (let x, let y):
    print("点的坐标是 (\(x), \(y))")
}

在这个例子中,(let x, let y) 是一个元组模式,其中 let xlet y 是值绑定模式,它们分别将元组中的第一个和第二个元素绑定到常量 xy 中。

值绑定模式也可以在枚举用例模式中使用。考虑以下枚举:

enum Shape {
    case point
    case circle(center: (x: Double, y: Double), radius: Double)
    case rectangle(origin: (x: Double, y: Double), width: Double, height: Double)
}

let myShape = Shape.circle(center: (1.0, 2.0), radius: 5.0)

switch myShape {
case .point:
    print("这是一个点")
case let .circle(center, radius):
    print("这是一个圆,圆心坐标为 (\(center.x), \(center.y)),半径为 \(radius)")
case let .rectangle(origin, width, height):
    print("这是一个矩形,原点坐标为 (\(origin.x), \(origin.y)),宽为 \(width),高为 \(height)")
}

在这个例子中,let .circle(center, radius)let .rectangle(origin, width, height) 都是枚举用例模式,其中使用了值绑定模式将关联值绑定到常量中。

2.5 元组模式

元组模式用于匹配元组类型的值,并可以嵌套其他模式。元组模式由逗号分隔的多个模式组成,用括号括起来。

例如:

let coordinates = (3, 5)

switch coordinates {
case (0, 0):
    print("坐标在原点")
case (let x, 0):
    print("坐标在 x 轴上,x 值为 \(x)")
case (0, let y):
    print("坐标在 y 轴上,y 值为 \(y)")
case (let x, let y):
    print("坐标在其他位置,x 值为 \(x),y 值为 \(y)")
}

在这个例子中,switch 语句的 case 分支使用了不同的元组模式进行匹配:

  • 第一个 case 分支匹配原点 (0, 0)。
  • 第二个 case 分支匹配 y 坐标为 0 的点,并将 x 坐标绑定到常量 x 中。
  • 第三个 case 分支匹配 x 坐标为 0 的点,并将 y 坐标绑定到常量 y 中。
  • 第四个 case 分支匹配所有其他点,并将 x 和 y 坐标分别绑定到常量 x 和 y 中。

元组模式可以嵌套其他模式,例如:

let complexTuple = (1, ("apple", "banana"), 3)

switch complexTuple {
case (let a, (let b, let c), let d):
    print("a = \(a), b = \(b), c = \(c), d = \(d)")
}

在这个例子中,元组模式 (let a, (let b, let c), let d) 嵌套了另一个元组模式 (let b, let c),用于匹配复杂元组中的子元组。

2.6 枚举用例模式

枚举用例模式用于匹配枚举类型的特定用例。枚举用例模式可以有以下两种形式:

  1. 简单枚举用例模式:只匹配特定的枚举用例,不关心关联值。
  2. 带关联值的枚举用例模式:匹配特定的枚举用例,并提取关联值。

例如,考虑以下枚举:

enum Result {
    case success
    case failure(String)
}

let operationResult: Result = .failure("操作失败")

switch operationResult {
case .success:
    print("操作成功")
case .failure(let errorMessage):
    print("操作失败,错误信息: \(errorMessage)")
}

在这个例子中,case .success 是一个简单枚举用例模式,用于匹配 Result.success 用例;case .failure(let errorMessage) 是一个带关联值的枚举用例模式,用于匹配 Result.failure 用例,并将关联的错误信息绑定到常量 errorMessage 中。

枚举用例模式在处理 Swift 的可选类型时特别有用。可选类型实际上是一个枚举,有两个用例:.none(对应 nil)和 .some(Wrapped)(对应有值的情况)。因此,可以使用枚举用例模式来匹配可选类型的值:

let optionalValue: Int? = 42

switch optionalValue {
case .none:
    print("值为 nil")
case .some(let value):
    print("值为 \(value)")
}

在这个例子中,case .none 匹配可选值为 nil 的情况,case .some(let value) 匹配可选值有值的情况,并将值绑定到常量 value 中。

2.7 可选模式

可选模式是一种特殊的模式,用于匹配可选类型的值。可选模式实际上是枚举用例模式的语法糖,用于更简洁地匹配可选类型。

可选模式的语法是在模式后面加上问号(?)。例如:

let optionalValue: Int? = 42

switch optionalValue {
case nil:
    print("值为 nil")
case let value?:
    print("值为 \(value)")
}

在这个例子中,case let value? 是一个可选模式,它等价于 case .some(let value)。可选模式提供了一种更简洁的方式来匹配可选类型的值。

可选模式也可以在其他上下文中使用,例如 for-in 循环:

let arrayOfOptionals: [Int?] = [1, nil, 3, nil, 5]

for case let value? in arrayOfOptionals {
    print("非 nil 值: \(value)")
}

在这个例子中,for case let value? in arrayOfOptionals 只遍历数组中非 nil 的元素,并将它们的值绑定到常量 value 中。

2.8 类型转换模式

类型转换模式用于检查值的类型并进行类型转换。Swift 提供了两种类型转换模式:

  1. is 模式:用于检查值是否是某种类型。
  2. as 模式:用于检查值是否是某种类型,并将其转换为该类型。

is 模式的语法是 is Type,例如:

let anyValue: Any = "hello"

switch anyValue {
case is Int:
    print("值是整数")
case is String:
    print("值是字符串")
default:
    print("值是其他类型")
}

在这个例子中,case is String 检查 anyValue 是否是字符串类型。

as 模式的语法是 pattern as Type,例如:

let anyValue: Any = 42

switch anyValue {
case let value as Int:
    print("值是整数,值为 \(value)")
case let value as String:
    print("值是字符串,值为 \(value)")
default:
    print("值是其他类型")
}

在这个例子中,case let value as Int 检查 anyValue 是否是整数类型,如果是,则将其转换为整数类型并绑定到常量 value 中。

类型转换模式在处理多态类型的集合时特别有用,例如:

let shapes: [Shape] = [.point, .circle(center: (0, 0), radius: 5), .rectangle(origin: (0, 0), width: 10, height: 20)]

for shape in shapes {
    switch shape {
    case .point:
        print("处理点")
    case let .circle(center, radius):
        print("处理圆,圆心: \(center),半径: \(radius)")
    case let .rectangle(origin, width, height):
        print("处理矩形,原点: \(origin),宽: \(width),高: \(height)")
    }
}

在这个例子中,我们遍历一个包含不同形状的数组,并根据每个形状的具体类型进行不同的处理。

2.9 表达式模式

表达式模式用于通过比较操作符进行值的匹配。表达式模式通常用于 switch 语句中,与 ~= 操作符一起使用。

~= 操作符是 Swift 中的模式匹配操作符,它定义了一个值是否匹配一个模式。对于不同的类型,~= 操作符有不同的实现。

例如,在范围匹配中,~= 操作符用于检查一个值是否在某个范围内:

let number = 5

switch number {
case 1...10:
    print("数字在 1 到 10 之间")
default:
    print("数字不在 1 到 10 之间")
}

在这个例子中,case 1...10 实际上是一个表达式模式,它使用 ~= 操作符来检查 number 是否在范围 1…10 内。

我们也可以自定义 ~= 操作符来支持自定义的模式匹配。例如,定义一个自定义的字符串匹配模式:

func ~= (pattern: String, value: String) -> Bool {
    return value.contains(pattern)
}

let text = "Hello, world!"

switch text {
case "Hello":
    print("文本包含 'Hello'")
case "world":
    print("文本包含 'world'")
default:
    print("文本不包含 'Hello' 或 'world'")
}

在这个例子中,我们自定义了 ~= 操作符,使其检查一个字符串是否包含另一个字符串。这样,我们就可以在 switch 语句中使用自定义的字符串匹配模式。

表达式模式还可以与 where 子句结合使用,进一步过滤匹配条件。例如:

let point = (3, 5)

switch point {
case let (x, y) where x == y:
    print("点在 y = x 直线上")
case let (x, y) where x == -y:
    print("点在 y = -x 直线上")
default:
    print("点在其他位置")
}

在这个例子中,我们使用 where 子句来进一步过滤匹配条件,检查点是否在特定的直线上。

三、Swift Switch 高级特性

3.1 复合匹配

Swift 的 switch 语句允许在单个 case 分支中匹配多个模式,这种特性称为复合匹配。复合匹配使用逗号分隔多个模式,只要其中一个模式匹配成功,就会执行对应的代码块。

例如:

let number = 5

switch number {
case 1, 3, 5, 7, 9:
    print("数字是奇数")
case 2, 4, 6, 8, 10:
    print("数字是偶数")
default:
    print("数字不在 1 到 10 之间")
}

在这个例子中,第一个 case 分支使用复合匹配,匹配数字 1、3、5、7、9,只要 number 是这些数字中的任意一个,就会执行对应的代码块。

复合匹配可以用于各种模式,包括元组模式、枚举用例模式等。例如:

let point = (0, 5)

switch point {
case (0, 0):
    print("点在原点")
case (0, _), (_, 0):
    print("点在坐标轴上")
default:
    print("点在其他位置")
}

在这个例子中,第二个 case 分支使用复合匹配,匹配 x 坐标为 0 的点和 y 坐标为 0 的点,即所有在坐标轴上的点。

复合匹配还可以与值绑定和 where 子句结合使用。例如:

let value: Any = "hello"

switch value {
case is String, is Int:
    print("值是字符串或整数")
case let number as Double where number > 0:
    print("值是正双精度浮点数")
default:
    print("值是其他类型")
}

在这个例子中,第一个 case 分支使用复合匹配,匹配字符串类型和整数类型的值;第二个 case 分支使用值绑定和 where 子句,匹配正双精度浮点数。

3.2 值绑定与模式嵌套

值绑定是 Swift switch 语句的一个强大特性,它允许在匹配过程中将值绑定到变量或常量,以便在 case 分支的代码块中使用。值绑定可以与各种模式结合使用,包括元组模式、枚举用例模式等。

例如,在元组模式中使用值绑定:

let point = (3, 5)

switch point {
case (let x, 0):
    print("点在 x 轴上,x 值为 \(x)")
case (0, let y):
    print("点在 y 轴上,y 值为 \(y)")
case (let x, let y):
    print("点的坐标为 (\(x), \(y))")
}

在这个例子中,每个 case 分支都使用了值绑定,将匹配的坐标值绑定到常量 x 和 y 中。

值绑定还可以与模式嵌套结合使用,处理更复杂的数据结构。例如,考虑以下嵌套的元组结构:

let complexPoint = (name: "origin", coordinates: (0, 0))

switch complexPoint {
case let (name, (x, y)):
    print("点 \(name) 的坐标为 (\(x), \(y))")
}

在这个例子中,我们使用了嵌套的值绑定模式,将元组中的 name 字段和 coordinates 字段分别绑定到常量 name 和 (x, y) 中。

值绑定在处理枚举类型时也非常有用,特别是对于有关联值的枚举。例如:

enum Shape {
    case point
    case circle(center: (x: Double, y: Double), radius: Double)
    case rectangle(origin: (x: Double, y: Double), width: Double, height: Double)
}

let myShape = Shape.circle(center: (1.0, 2.0), radius: 5.0)

switch myShape {
case .point:
    print("这是一个点")
case let .circle(center, radius):
    print("这是一个圆,圆心坐标为 (\(center.x), \(center.y)),半径为 \(radius)")
case let .rectangle(origin, width, height):
    print("这是一个矩形,原点坐标为 (\(origin.x), \(origin.y)),宽为 \(width),高为 \(height)")
}

在这个例子中,我们使用值绑定来提取枚举关联值中的各个部分,并在 case 分支的代码块中使用这些值。

3.3 where 子句

Swift 的 switch 语句允许在 case 分支中使用 where 子句来进一步过滤匹配条件。where 子句跟在模式后面,用于指定一个额外的布尔条件,只有当模式匹配成功且 where 子句的条件为 true 时,才会执行对应的代码块。

例如:

let number = 5

switch number {
case let x where x > 0 && x < 10:
    print("数字在 1 到 9 之间")
case let x where x >= 10 && x < 20:
    print("数字在 10 到 19 之间")
default:
    print("数字不在 1 到 19 之间")
}

在这个例子中,每个 case 分支都使用了 where 子句来进一步限制匹配条件。

where 子句可以与各种模式结合使用,包括元组模式、枚举用例模式等。例如:

let point = (3, 5)

switch point {
case let (x, y) where x == y:
    print("点在 y = x 直线上")
case let (x, y) where x == -y:
    print("点在 y = -x 直线上")
default:
    print("点在其他位置")
}

在这个例子中,我们使用 where 子句来检查点是否在特定的直线上。

where 子句在处理枚举类型时也非常有用。例如:

enum Result {
    case success(value: Int)
    case failure(error: String)
}

let result: Result = .success(value: 42)

switch result {
case let .success(value) where value > 50:
    print("成功,值大于 50")
case let .success(value):
    print("成功,值不大于 50")
case let .failure(error):
    print("失败,错误信息: \(error)")
}

在这个例子中,我们使用 where 子句来进一步过滤成功结果的值。

where 子句还可以引用之前在模式中绑定的变量。例如:

let numbers = [1, 2, 3, 4, 5]

for case let number? in numbers where number % 2 == 0 {
    print("偶数: \(number)")
}

在这个例子中,我们使用 where 子句来过滤数组中的偶数元素。

3.4 范围匹配

Swift 的 switch 语句支持使用范围操作符进行范围匹配,这使得我们可以方便地检查一个值是否在某个范围内。

范围匹配使用闭区间操作符 ... 或半开区间操作符 ..< 来定义范围。例如:

let number = 5

switch number {
case 1...5:
    print("数字在 1 到 5 之间")
case 6...10:
    print("数字在 6 到 10 之间")
default:
    print("数字不在 1 到 10 之间")
}

在这个例子中,第一个 case 分支使用闭区间操作符 ... 定义了一个从 1 到 5 的范围,第二个 case 分支定义了一个从 6 到 10 的范围。

范围匹配可以用于各种类型,包括整数、浮点数、字符、字符串等。例如:

let score = 85.5

switch score {
case 0..<60:
    print("不及格")
case 60..<80:
    print("良好")
case 80...100:
    print("优秀")
default:
    print("无效分数")
}

在这个例子中,我们使用范围匹配来评估一个分数的等级。

字符和字符串也可以使用范围匹配。例如:

let character: Character = "c"

switch character {
case "a"..."z":
    print("小写字母")
case "A"..."Z":
    print("大写字母")
case "0"..."9":
    print("数字")
default:
    print("其他字符")
}

在这个例子中,我们使用范围匹配来判断一个字符的类型。

范围匹配还可以与值绑定和 where 子句结合使用。例如:

let point = (3, 5)

switch point {
case let (x, y) where x >= 0 && x <= 10 && y >= 0 && y <= 10:
    print("点在第一象限的 10x10 正方形内")
default:
    print("点在其他位置")
}

在这个例子中,我们使用 where 子句和范围判断来检查点是否在特定的区域内。

3.5 类型模式匹配

Swift 的 switch 语句支持类型模式匹配,这使得我们可以根据值的类型来执行不同的代码分支。类型模式匹配主要通过 isas 操作符来实现。

is 操作符用于检查一个值是否是某种类型,返回一个布尔值。例如:

let anyValue: Any = "hello"

switch anyValue {
case is String:
    print("值是字符串")
case is Int:
    print("值是整数")
default:
    print("值是其他类型")
}

在这个例子中,第一个 case 分支使用 is String 来检查 anyValue 是否是字符串类型。

as 操作符用于检查一个值是否是某种类型,并将其转换为该类型。如果转换成功,则返回转换后的值;如果转换失败,则返回 nil。例如:

let anyValue: Any = 42

switch anyValue {
case let value as String:
    print("值是字符串,值为 \(value)")
case let value as Int:
    print("值是整数,值为 \(value)")
default:
    print("值是其他类型")
}

在这个例子中,第二个 case 分支使用 let value as Int 来检查 anyValue 是否是整数类型,如果是,则将其转换为整数类型并绑定到常量 value 中。

类型模式匹配在处理多态集合时特别有用。例如:

protocol Shape {
    func area() -> Double
}

struct Circle: Shape {
    let radius: Double
    func area() -> Double {
        return Double.pi * radius * radius
    }
}

struct Rectangle: Shape {
    let width: Double
    let height: Double
    func area() -> Double {
        return width * height
    }
}

let shapes: [Shape] = [Circle(radius: 5), Rectangle(width: 10, height: 20)]

for shape in shapes {
    switch shape {
    case let circle as Circle:
        print("圆的面积: \(circle.area())")
    case let rectangle as Rectangle:
        print("矩形的面积: \(rectangle.area())")
    default:
        print("未知形状")
    }
}

在这个例子中,我们使用类型模式匹配来检查集合中的每个形状的具体类型,并调用相应的方法。

类型模式匹配还可以与 where 子句结合使用,进一步过滤匹配条件。例如:

let anyValue: Any = "hello"

switch anyValue {
case let value as String where value.count > 5:
    print("值是长度大于 5 的字符串")
case let value as String:
    print("值是长度小于等于 5 的字符串")
default:
    print("值不是字符串")
}

在这个例子中,我们使用 where 子句来进一步检查字符串的长度。

3.6 枚举匹配与关联值

Swift 的枚举类型可以有关联值,这使得枚举更加灵活和强大。Swift 的 switch 语句可以很好地支持枚举匹配,包括匹配枚举的不同用例和提取关联值。

考虑以下枚举:

enum Result {
    case success
    case failure(String)
    case progress(Double)
}

这个枚举定义了三种可能的结果:成功、失败(带有错误信息)和进度(带有进度百分比)。

我们可以使用 switch 语句来匹配这些枚举用例:

let result: Result = .progress(0.75)

switch result {
case .success:
    print("操作成功")
case .failure(let errorMessage):
    print("操作失败,错误信息: \(errorMessage)")
case .progress(let percentage):
    print("操作进度: \(percentage * 100)%")
}

在这个例子中,每个 case 分支匹配不同的枚举用例,并提取关联值(如果有的话)。

对于有多个关联值的枚举用例,我们可以使用元组模式来匹配和提取这些值。例如:

enum Shape {
    case point
    case circle(center: (x: Double, y: Double), radius: Double)
    case rectangle(origin: (x: Double, y: Double), width: Double, height: Double)
}

let myShape = Shape.rectangle(origin: (0, 0), width: 10, height: 20)

switch myShape {
case .point:
    print("这是一个点")
case let .circle(center, radius):
    print("这是一个圆,圆心坐标为 (\(center.x), \(center.y)),半径为 \(radius)")
case let .rectangle((x, y), width, height):
    print("这是一个矩形,原点坐标为 (\(x), \(y)),宽为 \(width),高为 \(height)")
}

在这个例子中,第三个 case 分支使用元组模式来匹配矩形的原点坐标,并直接提取 x 和 y 值。

枚举匹配还可以与 where 子句结合使用,进一步过滤匹配条件。例如:

enum Result {
    case success(value: Int)
    case failure(error: String)
}

let result: Result = .success(value: 42)

switch result {
case let .success(value) where value > 50:
    print("成功,值大于 50")
case let .success(value):
    print("成功,值不大于 50")
case let .failure(error):
    print("失败,错误信息: \(error)")
}

在这个例子中,我们使用 where 子句来进一步过滤成功结果的值。

3.7 可选值匹配

Swift 的可选类型是一种特殊的枚举,它有两个用例:.none(对应 nil)和 .some(Wrapped)(对应有值的情况)。因此,我们可以使用 switch 语句来匹配可选类型的值。

最简单的可选值匹配方式是直接匹配 nil 和非 nil 情况:

let optionalValue: Int? = 42

switch optionalValue {
case nil:
    print("值为 nil")
case let .some(value):
    print("值为 \(value)")
}

在这个例子中,第一个 case 分支匹配 nil 值,第二个 case 分支匹配非 nil 值,并将值绑定到常量 value 中。

Swift 还提供了一种更简洁的语法来匹配可选值,即使用可选模式(在模式后面加问号):

let optionalValue: Int? = 42

switch optionalValue {
case nil:
    print("值为 nil")
case let value?:
    print("值为 \(value)")
}

在这个例子中,case let value? 等价于 case let .some(value),提供了一种更简洁的方式来匹配可选值。

可选值匹配还可以与 where 子句结合使用,进一步过滤匹配条件。例如:

let optionalValue: Int? = 42

switch optionalValue {
case nil:
    print("值为 nil")
case let value? where value > 50:
    print("值大于 50")
case let value?:
    print("值不大于 50")
}

在这个例子中,我们使用 where 子句来进一步检查可选值的值。

可选值匹配在处理嵌套可选值时也非常有用。例如:

let nestedOptional: Int?? = .some(.some(42))

switch nestedOptional {
case nil:
    print("最外层为 nil")
case let .some(innerOptional):
    switch innerOptional {
    case nil:
        print("内层为 nil")
    case let .some(value):
        print("值为 \(value)")
    }
}

在这个例子中,我们使用嵌套的 switch 语句来处理嵌套可选值。

3.8 嵌套 Switch 语句

Swift 允许在 switch 语句的 case 分支中嵌套另一个 switch 语句,这使得我们可以处理更复杂的匹配逻辑。

例如,考虑以下嵌套的元组结构:

let complexPoint = (name: "origin", coordinates: (x: 0, y: 0))

switch complexPoint {
case let (name, (x, y)):
    switch (x, y) {
    case (0, 0):
        print("点 \(name) 在原点")
    case (0, _):
        print("点 \(name) 在 y 轴上")
    case (_, 0):
        print("点 \(name) 在 x 轴上")
    default:
        print("点 \(name) 在其他位置")
    }
}

在这个例子中,外层 switch 语句匹配整个 complexPoint 元组,并将其分解为 name 和 coordinates。内层 switch 语句则根据 coordinates 的值进行进一步的匹配。

嵌套 switch 语句在处理多个枚举组合时也非常有用。例如:

enum Color {
    case red, green, blue
}

enum Size {
    case small, medium, large
}

let product = (color: Color.red, size: Size.medium)

switch product {
case let (color, size):
    switch (color, size) {
    case (.red, .small):
        print("红色小号产品")
    case (.red, .medium):
        print("红色中号产品")
    case (.red, .large):
        print("红色大号产品")
    case (.green, .small):
        print("绿色小号产品")
    case (.green, .medium):
        print("绿色中号产品")
    case (.green, .large):
        print("绿色大号产品")
    case (.blue, .small):
        print("蓝色小号产品")
    case (.blue, .medium):
        print("蓝色中号产品")
    case (.blue, .large):
        print("蓝色大号产品")
    }
}

在这个例子中,外层 switch 语句将 product 元组分解为 color 和 size,内层 switch 语句则根据 color 和 size 的组合进行匹配。

嵌套 switch 语句还可以与值绑定、where 子句等特性结合使用,实现更复杂的匹配逻辑。例如:

let number: Any = 42

switch number {
case let value as Int:
    switch value {
    case let x where x < 0:
        print("负整数")
    case let x where x % 2 == 0:
        print("正偶数")
    default:
        print("正奇数")
    }
case let value as String:
    print("字符串: \(value)")
default:
    print("其他类型")
}

在这个例子中,外层 switch 语句首先判断 number 的类型,内层 switch 语句则根据整数的值进行进一步的分类。

3.9 Fallthrough 关键字

在 Swift 中,switch 语句默认情况下不会发生执行穿透(fallthrough),即执行完一个 case 分支后,不会继续执行下一个 case 分支。这与 C 和 Objective-C 中的 switch 语句行为不同。

如果需要在 Swift 的 switch 语句中实现执行穿透,可以使用 fallthrough 关键字。fallthrough 关键字用于显式地指示程序继续执行下一个 case 分支,而不检查该分支的条件。

例如:

let number = 3

switch number {
case 1:
    print("数字是 1")
    fallthrough
case 2, 3:
    print("数字是 2 或 3")
case 4:
    print("数字是 4")
default:
    print("数字是其他值")
}

在这个例子中,当 number 的值为 3 时,程序会执行第二个 case 分支,打印 “数字是 2 或 3”。由于该分支中使用了 fallthrough 关键字,程序会继续执行下一个 case 分支(即 case 4),即使 number 的值不等于 4。因此,最终的输出结果为:

数字是 2 或 3
数字是 4

需要注意的是,fallthrough 关键字只能用于转移控制到下一个 case 分支,不能跳过后续的 case 分支直接跳到 default 分支。

fallthrough 关键字在处理需要多个 case 分支共享相同代码的情况时非常有用。例如:

let character: Character = "a"

switch character {
case "a", "e", "i", "o", "u":
    print("元音字母")
case "y":
    print("有时是元音,有时是辅音")
    fallthrough
default:
    print("辅音字母")
}

在这个例子中,当 character 的值为 “y” 时,程序会先执行 case “y” 分支,打印 “有时是元音,有时是辅音”,然后通过 fallthrough 关键字继续执行 default 分支,打印 “辅音字母”。

虽然 fallthrough 关键字提供了执行穿透的能力,但在实际编程中应谨慎使用,因为过度使用可能会导致代码难以理解和维护。在大多数情况下,应该优先使用复合匹配或重构代码来避免使用 fallthrough

3.10 标签化 Switch 语句

在 Swift 中,当存在嵌套的 switch 语句时,可以使用标签来明确指定要控制的是哪个 switch 语句。标签化的 switch 语句允许我们在嵌套结构中精确地控制流程跳转。

标签化 switch 语句的语法如下:

labelName: switch valueToMatch {
case pattern1:
    // 代码块
case pattern2:
    // 代码块
    break labelName  // 跳出指定标签的 switch 语句
default:
    // 默认代码块
}

例如,考虑以下嵌套的 switch 语句:

let outerValue = 1
let innerValue = 2

outerSwitch: switch outerValue {
case 1:
    print("外层值为 1")
    switch innerValue {
    case 1:
        print("内层值为 1")
    case 2:
        print("内层值为 2")
        break outerSwitch  // 跳出外层 switch 语句
    default:
        print("内层值为其他值")
    }
    print("外层 case 1 的后续代码")  // 不会执行,因为上面已经跳出外层 switch
case 2:
    print("外层值为 2")
default:
    print("外层值为其他值")
}

print("switch 语句后的代码")  // 会执行

在这个例子中,当 innerValue 的值为 2 时,内层 switch 语句中的 break outerSwitch 会导致程序跳出外层 switch 语句,因此 “外层 case 1 的后续代码” 不会被执行。

标签化 switch 语句在处理复杂的嵌套结构时非常有用,特别是当需要从深层嵌套中跳出时。例如:

let x = 1
let y = 2

mainSwitch: switch x {
case 1:
    print("x 为 1")
    switch y {
    case 1:
        print("y 为 1")
    case 2:
        print("y 为 2")
        switch y * 2 {
        case 4:
            print("y 的两倍为 4")
            break mainSwitch  // 跳出最外层 switch 语句
        default:
            print("y 的两倍为其他值")
        }
    default:
        print("y 为其他值")
    }
case 2:
    print("x 为 2")
default:
    print("x 为其他值")
}

print("继续执行后续代码")

在这个例子中,当 y 的值为 2 时,内层 switch 语句会计算 y 的两倍(即 4),然后通过 break mainSwitch 跳出最外层的 switch 语句。

需要注意的是,标签化 switch 语句中的标签必须遵循 Swift 的标识符命名规则,并且通常使用大写字母开头以提高可读性。标签化 switch 语句不仅可以与 break 语句一起使用,还可以与 continue 语句(在循环中)一起使用,以精确控制流程跳转。

四、Swift Switch 源码分析

4.1 编译时处理

Swift 的 switch 语句在编译时会经历多个处理阶段,包括词法分析、语法分析、语义分析和代码生成等。下面我们从源码级别分析这些处理阶段。

4.1.1 词法分析

词法分析是编译过程的第一个阶段,它将源代码分解为一个个的词法单元(Token)。在 Swift 编译器中,词法分析器由 Lexer 类实现,位于 lib/Syntax/Lexer.cpp 文件中。

当遇到 switch 语句时,词法分析器会识别以下关键字和符号:

  • switch 关键字
  • 左括号 (
  • 右括号 )
  • 左大括号 {
  • 右大括号 }
  • case 关键字
  • default 关键字
  • 冒号 :
  • 逗号 ,
  • fallthrough 关键字(如果存在)

例如,对于以下 switch 语句:

switch number {
case 1:
    print("数字是 1")
case 2, 3:
    print("数字是 2 或 3")
default:
    print("数字是其他值")
}

词法分析器会将其分解为以下词法单元:

  • switch
  • (
  • number
  • )
  • {
  • case
  • 1
  • :
  • print
  • (
  • "数字是 1"
  • )
  • case
  • 2
  • ,
  • 3
  • :
  • print
  • (
  • "数字是 2 或 3"
  • )
  • default
  • :
  • print
  • (
  • "数字是其他值"
  • )
  • }
4.1.2 语法分析

语法分析是编译过程的第二个阶段,它将词法单元组合成抽象语法树(AST)。在 Swift 编译器中,语法分析器由 Parser 类实现,位于 lib/Parse/Parser.cpp 文件中。

当解析 switch 语句时,语法分析器会构建一个 SwitchStmt 节点,该节点包含以下信息:

  • 控制表达式(即 switch 后面括号中的表达式)
  • 多个 CaseItem 节点,每个节点表示一个 case 分支
  • 一个可选的 DefaultItem 节点,表示 default 分支

SwitchStmt 节点的定义如下(简化版):

class SwitchStmt : public Stmt {
  SourceLoc SwitchLoc;        // switch 关键字的位置
  Expr *ControlExpr;          // 控制表达式
  SourceLoc LBraceLoc;        // 左大括号的位置
  SmallVector<CaseItem, 4> Cases;  // case 分支列表
  Optional<DefaultItem> Default;  // default 分支(可选)
  SourceLoc RBraceLoc;        // 右大括号的位置
  
public:
  // 构造函数和访问器方法
};

每个 CaseItem 节点包含以下信息:

  • 一个或多个模式(Pattern)
  • 一个可选的 where 子句
  • 一个代码块(即 case 分支后面的大括号中的内容)

CaseItem 节点的定义如下(简化版):

struct CaseItem {
  SourceLoc CaseLoc;          // case 关键字的位置
  SmallVector<Pattern*, 2> Patterns;  // 模式列表
  Optional<Expr*> WhereClause;  // where 子句(可选)
  BraceStmt *Body;            // 代码块
  
  // 构造函数和访问器方法
};
4.1.3 语义分析

语义分析是编译过程的第三个阶段,它对抽象语法树进行类型检查和语义验证。在 Swift 编译器中,语义分析由 TypeChecker 类实现,位于 lib/Sema/TypeChecker.cpp 文件中。

对于 switch 语句,语义分析主要包括以下几个方面:

  1. 类型检查控制表达式:确保控制表达式的类型是可匹配的类型,如整数、枚举、可选类型等。

  2. 模式验证:检查每个 case 分支中的模式是否有效,并确保模式的类型与控制表达式的类型兼容。

  3. 穷尽性检查:确保所有可能的情况都被 case 分支覆盖,除非提供了 default 分支。这是 Swift switch 语句的一个重要特性,确保了代码的安全性。

  4. where 子句验证:检查 where 子句中的表达式是否为布尔类型。

  5. fallthrough 验证:如果 case 分支中使用了 fallthrough 关键字,确保它是该分支的最后一条语句。

例如,对于以下 switch 语句:

enum Color {
    case red, green, blue
}

let color: Color = .red

switch color {
case .red:
    print("红色")
case .green:
    print("绿色")
case .blue:
    print("蓝色")
}

语义分析器会检查:

  • 控制表达式 color 的类型是 Color 枚举。
  • 每个 case 分支中的模式(.red.green.blue)都是 Color 枚举的有效用例。
  • 所有可能的 Color 枚举用例都被覆盖,因此不需要 default 分支。

如果缺少某个 case 分支,例如:

switch color {
case .red:
    print("红色")
case .green:
    print("绿色")
}

语义分析器会报错,提示 Switch must be exhaustive(switch 语句必须穷尽所有情况)。

4.1.4 代码生成

代码生成是编译过程的最后一个阶段,它将抽象语法树转换为目标机器的代码。在 Swift 编译器中,代码生成由 SIL(Swift Intermediate Language)生成器和 LLVM 后端实现。

对于 switch 语句,代码生成阶段会根据模式的类型和复杂度生成不同的代码:

  1. 枚举匹配:对于枚举类型的匹配,编译器通常会生成一个跳转表(jump table),根据枚举值的索引直接跳转到对应的 case 分支。

  2. 整数匹配:对于整数类型的匹配,编译器可能会生成一系列的比较指令,或者如果值的范围比较小且连续,也会生成跳转表。

  3. 范围匹配:对于范围匹配,编译器会生成比较指令来检查值是否在范围内。

  4. 复杂模式匹配:对于包含值绑定、where 子句等复杂模式的匹配,编译器会生成更复杂的逻辑来执行匹配和条件检查。

例如,对于以下简单的整数匹配:

let number = 2

switch number {
case 1:
    print("数字是 1")
case 2:
    print("数字是 2")
case 3:
    print("数字是 3")
default:
    print("数字是其他值")
}

编译器可能会生成类似于以下的伪代码:

if number == 1:
    跳转到 case 1 代码块
else if number == 2:
    跳转到 case 2 代码块
else if number == 3:
    跳转到 case 3 代码块
else:
    跳转到 default 代码块

而对于枚举匹配:

enum Color {
    case red, green, blue
}

let color: Color = .green

switch color {
case .red:
    print("红色")
case .green:
    print("绿色")
case .blue:
    print("蓝色")
}

编译器可能会生成一个跳转表,根据枚举值的内部表示直接跳转到对应的 case 分支,这样可以提高匹配效率。

4.2 运行时行为

Swift 的 switch 语句在运行时的行为取决于编译时生成的代码。下面我们从源码级别分析不同类型的 switch 语句在运行时的行为。

4.2.1 枚举匹配的运行时行为

对于枚举类型的 switch 语句,编译器通常会生成一个跳转表(jump table)来实现高效的匹配。枚举类型在 Swift 中通常有一个内部的整数值表示,称为 raw value。

例如,考虑以下枚举:

enum Color {
    case red, green, blue
}

在内存中,.red 可能表示为 0,.green 表示为 1,.blue 表示为 2。当我们使用 switch 语句匹配这个枚举时:

let color: Color = .green

switch color {
case .red:
    print("红色")
case .green:
    print("绿色")
case .blue:
    print("蓝色")
}

编译器会生成一个跳转表,根据枚举值的内部表示直接跳转到对应的 case 分支。在运行时,这个过程大致如下:

  1. 获取枚举值的内部表示(例如,.green 的内部表示为 1)。
  2. 使用这个内部表示作为索引访问跳转表。
  3. 从跳转表中获取对应的代码地址并跳转执行。

这种实现方式的时间复杂度是 O(1),非常高效。

4.2.2 整数匹配的运行时行为

对于整数类型的 switch 语句,编译器会根据值的分布和范围选择不同的实现方式。

如果整数值的范围较小且连续,编译器可能会生成一个跳转表,类似于枚举匹配的实现方式。例如:

let number = 2

switch number {
case 1:
    print("数字是 1")
case 2:
    print("数字是 2")
case 3:
    print("数字是 3")
default:
    print("数字是其他值")
}

编译器可能会生成一个包含 3 个条目的跳转表,索引 0 对应值 1,索引 1 对应值 2,索引 2 对应值 3。在运行时,程序会计算 number - 1 作为索引,然后从跳转表中获取对应的代码地址。

如果整数值的范围较大或不连续,编译器可能会生成一系列的比较指令。例如:

let number = 100

switch number {
case 1:
    print("数字是 1")
case 100:
    print("数字是 100")
case 1000:
    print("数字是 1000")
default:
    print("数字是其他值")
}

编译器可能会生成类似于以下的伪代码:

if number == 1:
    跳转到 case 1 代码块
else if number == 100:
    跳转到 case 100 代码块
else if number == 1000:
    跳转到 case 1000 代码块
else:
    跳转到 default 代码块

这种实现方式的时间复杂度是 O(n),其中 n 是 case 分支的数量。

4.2.3 范围匹配的运行时行为

对于范围匹配的 switch 语句,编译器会生成比较指令来检查值是否在范围内。例如:

let number = 25

switch number {
case 1...10:
    print("数字在 1 到 10 之间")
case 11...20:
    print("数字在 11 到 20 之间")
case 21...30:
    print("数字在 21 到 30 之间")
default:
    print("数字不在 1 到 30 之间")
}

编译器可能会生成类似于以下的伪代码:

if number >= 1 && number <= 10:
    跳转到 case 1...10 代码块
else if number >= 11 && number <= 20:
    跳转到 case 11...20 代码块
else if number >= 21 && number <= 30:
    跳转到 case 21...30 代码块
else:
    跳转到 default 代码块

对于每个范围匹配,编译器会生成两个比较指令(大于等于和小于等于)。如果有多个范围匹配,这些比较指令会按顺序执行,直到找到匹配的范围或执行 default 分支。

4.2.4 复杂模式匹配的运行时行为

对于包含值绑定、where 子句等复杂模式的 switch 语句,编译器会生成更复杂的逻辑来执行匹配和条件检查。

例如,考虑以下包含 where 子句的 switch 语句:

let point = (3, 5)

switch point {
case let (x, y) where x == y:
    print("点在 y = x 直线上")
case let (x, y) where x == -y:
    print("点在 y = -x 直线上")
default:
    print("点在其他位置")
}

编译器可能会生成类似于以下的伪代码:

将 point 分解为 x 和 y
if x == y:
    跳转到 case let (x, y) where x == y 代码块
else if x == -y:
    跳转到 case let (x, y) where x == -y 代码块
else:
    跳转到 default 代码块

在运行时,程序会先执行值绑定(将元组分解为 x 和 y),然后执行 where 子句中的条件检查。如果条件为真,则执行对应的 case 分支;否则继续检查下一个 case 分支。

对于包含类型转换的模式匹配,编译器会生成类型检查和转换的代码。例如:

let anyValue: Any = 42

switch anyValue {
case let value as String:
    print("值是字符串,值为 \(value)")
case let value as Int:
    print("值是整数,值为 \(value)")
default:
    print("值是其他类型")
}

编译器可能会生成类似于以下的伪代码:

if anyValue 是 String 类型:
    将 anyValue 转换为 String 类型并赋值给 value
    跳转到 case let value as String 代码块
else if anyValue 是 Int 类型:
    将 anyValue 转换为 Int 类型并赋值给 value
    跳转到 case let value as Int 代码块
else:
    跳转到 default 代码块

在运行时,程序会先检查 anyValue 的实际类型,然后进行相应的类型转换和分支跳转。

4.3 与其他语言的对比

4.3.1 与 C 语言的对比

Swift 的 switch 语句与 C 语言的 switch 语句有以下主要区别:

  1. 穷尽性检查:Swift 要求 switch 语句必须覆盖所有可能的情况,除非提供了 default 分支。而 C 语言没有这个要求,可能会导致未处理的情况。

  2. 不允许隐式贯穿:在 Swift 中,执行完一个 case 分支后不会继续执行下一个 case 分支,不需要使用 break 语句。而在 C 语言中,默认情况下会发生执行穿透,需要显式使用 break 语句来避免。

  3. 模式匹配的多样性:Swift 的 switch 语句支持多种模式匹配,包括范围匹配、元组匹配、枚举关联值匹配、类型转换匹配等。而 C 语言的 switch 语句只能匹配整数类型(包括字符类型)。

  4. where 子句:Swift 的 switch 语句支持 where 子句,可以进一步过滤匹配条件。而 C 语言没有这个特性。

  5. 类型安全性:Swift 的 switch 语句是类型安全的,编译器会检查模式的类型与控制表达式的类型是否兼容。而 C 语言的 switch 语句在类型检查方面较为宽松,可能会导致运行时错误。

例如,以下是 C 语言的 switch 语句示例:

int number = 2;

switch (number) {
case 1:
    printf("数字是 1\n");
    break;
case 2:
    printf("数字是 2\n");
    // 没有 break,会发生执行穿透
case 3:
    printf("数字是 3\n");
    break;
default:
    printf("数字是其他值\n");
}

如果 number 的值为 2,输出结果将是:

数字是 2
数字是 3

而在 Swift 中,同样的代码不会发生执行穿透:

let number = 2

switch number {
case 1:
    print("数字是 1")
case 2:
    print("数字是 2")
case 3:
    print("数字是 3")
default:
    print("数字是其他值")
}

输出结果将是:

数字是 2
4.3.2 与 Java 的对比

Swift 的 switch 语句与 Java 的 switch 语句有以下主要区别:

  1. 支持的类型:Swift 的 switch 语句可以匹配多种类型,包括整数、浮点数、字符串、枚举、元组、可选类型等。而 Java 的 switch 语句在 Java 7 之前只能匹配整数类型(包括字符类型),Java 7 及以后可以匹配字符串类型,但仍然不支持浮点数、元组等类型。

  2. 穷尽性检查:Swift 要求 switch 语句必须覆盖所有可能的情况,除非提供了 default 分支。而 Java 没有这个要求,可能会导致未处理的情况。

  3. 不允许隐式贯穿:在 Swift 中,执行完一个 case 分支后不会继续执行下一个 case 分支,不需要使用 break 语句。而在 Java 中,默认情况下会发生执行穿透,需要显式使用 break 语句来避免。

  4. 模式匹配:Swift 的 switch 语句支持强大的模式匹配,包括范围匹配、元组匹配、枚举关联值匹配、类型转换匹配等。而 Java 的 switch 语句只能进行简单的值匹配。

  5. 返回值:Swift 的 switch 语句可以作为表达式返回值,而 Java 的 switch 语句不能直接返回值。

例如,以下是 Java 的 switch 语句示例:

int number = 2;

switch (number) {
case 1:
    System.out.println("数字是 1");
    break;
case 2:
    System.out.println("数字是 2");
    break;
case 3:
    System.out.println("数字是 3");
    break;
default:
    System.out.println("数字是其他值");
}

而在 Swift 中,同样的代码可以更简洁:

let number = 2

switch number {
case 1:
    print("数字是 1")
case 2:
    print("数字是 2")
case 3:
    print("数字是 3")
default:
    print("数字是其他值")
}

此外,Swift 还支持更复杂的模式匹配,例如:

let point = (3, 5)

switch point {
case (0, 0):
    print("点在原点")
case (0, _):
    print("点在 y 轴上")
case (_, 0):
    print("点在 x 轴上")
case let (x, y) where x == y:
    print("点在 y = x 直线上")
default:
    print("点在其他位置")
}

这种复杂的模式匹配在 Java 中是无法直接实现的。

4.3.3 与 Kotlin 的对比

Swift 的 switch 语句与 Kotlin 的 when 表达式有很多相似之处,因为它们都提供了强大的模式匹配功能。以下是它们的主要区别:

  1. 语法形式:Swift 使用 switch 关键字,而 Kotlin 使用 when 关键字。此外,Kotlin 的 when 可以作为表达式使用,也可以作为语句使用,而 Swift 的 switch 语句在 Swift 5.1 之前只能作为语句使用,Swift 5.1 及以后可以作为表达式使用。

  2. 默认行为:Swift 的 switch 语句默认不会发生执行穿透,而 Kotlin 的 when 表达式也不会发生执行穿透,不需要使用 break 语句。

  3. 穷尽性检查:Swift 和 Kotlin 都要求对枚举类型的匹配必须覆盖所有可能的情况,除非提供了 else 分支(Kotlin)或 default 分支(Swift)。

  4. 模式匹配:Swift 和 Kotlin 都支持多种模式匹配,包括值匹配、范围匹配、类型转换匹配等。但 Swift 的模式匹配更加灵活,例如支持元组模式匹配、枚举关联值匹配等。

  5. 与 null 的交互:Kotlin 有内置的空安全机制,when 表达式可以直接处理 null 值。而 Swift 的 switch 语句需要显式处理可选类型。

例如,以下是 Kotlin 的 when 表达式示例:

val number = 2

when (number) {
    1 -> println("数字是 1")
    2 -> println("数字是 2")
    3 -> println("数字是 3")
    else -> println("数字是其他值")
}

而在 Swift 中,同样的代码为:

let number = 2

switch number {
case 1:
    print("数字是 1")
case 2:
    print("数字是 2")
case 3:
    print("数字是 3")
default:
    print("数字是其他值")
}

Kotlin 的 when 表达式还可以作为表达式返回值:

val message = when (number) {
    1 -> "数字是 1"
    2 -> "数字是 2"
    3 -> "数字是 3"
    else -> "数字是其他值"
}

在 Swift 5.1 及以后,也可以这样做:

let message = switch number {
case 1: "数字是 1"
case 2: "数字是 2"
case 3: "数字是 3"
default: "数字是其他值"
}

总的来说,Swift 和 Kotlin 的模式匹配功能都非常强大,但 Swift 的模式匹配在某些方面更加灵活,例如对元组和枚举关联值的支持。

4.4 优化与性能

Swift 的 switch 语句在编译时会进行多种优化,以提高匹配效率。下面我们从源码级别分析这些优化。

4.4.1 跳转表优化

对于枚举类型和整数值范围较小且连续的 switch 语句,编译器会生成跳转表(jump table)来实现高效的匹配。跳转表是一种数据结构,它将输入值映射到对应的代码地址,使得匹配过程的时间复杂度为 O(1)。

例如,对于以下枚举匹配:

enum Color {
    case red, green, blue
}

let color: Color = .green

switch color {
case .red:
    print("红色")
case .green:
    print("绿色")
case .blue:
    print("蓝色")
}

编译器会生成一个包含 3 个条目的跳转表,索引 0 对应 .red,索引 1 对应 .green,索引 2 对应 .blue。在运行时,程序会根据 color 的内部表示(0、1 或 2)直接访问跳转表,获取对应的代码地址并跳转执行。

这种优化在 SIL(Swift Intermediate Language)生成阶段实现,相关代码位于 lib/SILOptimizer/Utils/ConvertSwitchEnum.cpp 文件中。

4.4.2 二分查找优化

对于整数值范围较大但有序的 switch 语句,编译器会使用二分查找算法来优化匹配过程,将时间复杂度从 O(n) 降低到 O(log n)。

例如,对于以下 switch 语句:

let number = 50

switch number {
case 1:
    print("数字是 1")
case 10:
    print("数字是 10")
case 20:
    print("数字是 20")
case 30:
    print("数字是 30")
case 40:
    print("数字是 40")
case 50:
    print("数字是 50")
case 60:
    print("数字是 60")
case 70:
    print("数字是 70")
case 80:
    print("数字是 80")
case 90:
    print("数字是 90")
case 100:
    print("数字是 100")
default:
    print("数字是其他值")
}

编译器会将 case 分支中的值排序,然后使用二分查找算法来快速定位匹配的值。在运行时,程序会进行一系列的比较,每次将搜索范围缩小一半,直到找到匹配的值或确定没有匹配的值。

这种优化在 SIL 生成阶段实现,相关代码位于 lib/SILOptimizer/Utils/ConvertSwitchInt.cpp 文件中。

4.4.3 范围合并优化

对于包含多个连续范围的 switch 语句,编译器会尝试合并相邻的范围,减少比较次数。

例如,对于以下 switch 语句:

let number = 25

switch number {
case 1...10:
    print("数字在 1 到 10 之间")
case 11...20:
    print("数字在 11 到 20 之间")
case 21...30:
    print("数字在 21 到 30 之间")
case 31...40:
    print("数字在 31 到 40 之间")
default:
    print
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Android 小码蜂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值