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是要进行匹配的值。pattern1、pattern2等是匹配模式,可以是单个模式或多个模式的组合。- 每个 case 分支后面可以跟随一个或多个语句。
- default 分支是可选的,但如果所有可能的情况没有被 case 分支覆盖,则必须提供 default 分支。
1.3 核心特性
Swift switch 语句的核心特性包括:
-
模式匹配的多样性:可以匹配多种类型和模式,包括值绑定、范围匹配、类型转换等。
-
安全性:必须覆盖所有可能的情况,否则会导致编译错误,除非提供了 default 分支。
-
不允许隐式贯穿:默认情况下,执行完一个 case 分支后不会继续执行下一个 case 分支,不需要使用 break 语句。
-
复合匹配:单个 case 分支可以匹配多个模式,用逗号分隔。
-
值绑定:可以在模式中绑定常量或变量,以便在 case 分支的代码块中使用。
-
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 官方文档,模式可以分为以下几类:
-
通配符模式(Wildcard Pattern):使用下划线(_)表示,匹配并忽略任何值。
-
标识符模式(Identifier Pattern):用于匹配并将值绑定到变量或常量。
-
值绑定模式(Value-Binding Pattern):用于在匹配过程中将值绑定到变量或常量。
-
元组模式(Tuple Pattern):用于匹配元组类型的值,并可以嵌套其他模式。
-
枚举用例模式(Enumeration Case Pattern):用于匹配枚举类型的特定用例。
-
可选模式(Optional Pattern):用于匹配可选类型的值。
-
类型转换模式(Type-Casting Pattern):用于检查值的类型并进行类型转换。
-
表达式模式(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"
在这些例子中,x 和 y 都是标识符模式,它们分别匹配并绑定了右边的值。
标识符模式也可以在 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 Int 和 let text as String 都是标识符模式,它们分别将匹配的值绑定到常量 number 和 text 中。
2.4 值绑定模式
值绑定模式是标识符模式的一种扩展,它允许在匹配过程中将值绑定到变量或常量。值绑定模式通常用于元组模式、枚举用例模式等复杂模式中。
例如,在元组模式中使用值绑定:
let point = (3, 5)
switch point {
case (let x, let y):
print("点的坐标是 (\(x), \(y))")
}
在这个例子中,(let x, let y) 是一个元组模式,其中 let x 和 let y 是值绑定模式,它们分别将元组中的第一个和第二个元素绑定到常量 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)")
}
在这个例子中,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 枚举用例模式
枚举用例模式用于匹配枚举类型的特定用例。枚举用例模式可以有以下两种形式:
- 简单枚举用例模式:只匹配特定的枚举用例,不关心关联值。
- 带关联值的枚举用例模式:匹配特定的枚举用例,并提取关联值。
例如,考虑以下枚举:
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 提供了两种类型转换模式:
is模式:用于检查值是否是某种类型。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 语句支持类型模式匹配,这使得我们可以根据值的类型来执行不同的代码分支。类型模式匹配主要通过 is 和 as 操作符来实现。
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){case1:print("数字是 1")case2,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 语句,语义分析主要包括以下几个方面:
-
类型检查控制表达式:确保控制表达式的类型是可匹配的类型,如整数、枚举、可选类型等。
-
模式验证:检查每个 case 分支中的模式是否有效,并确保模式的类型与控制表达式的类型兼容。
-
穷尽性检查:确保所有可能的情况都被 case 分支覆盖,除非提供了 default 分支。这是 Swift switch 语句的一个重要特性,确保了代码的安全性。
-
where 子句验证:检查 where 子句中的表达式是否为布尔类型。
-
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 语句,代码生成阶段会根据模式的类型和复杂度生成不同的代码:
-
枚举匹配:对于枚举类型的匹配,编译器通常会生成一个跳转表(jump table),根据枚举值的索引直接跳转到对应的 case 分支。
-
整数匹配:对于整数类型的匹配,编译器可能会生成一系列的比较指令,或者如果值的范围比较小且连续,也会生成跳转表。
-
范围匹配:对于范围匹配,编译器会生成比较指令来检查值是否在范围内。
-
复杂模式匹配:对于包含值绑定、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 分支。在运行时,这个过程大致如下:
- 获取枚举值的内部表示(例如,.green 的内部表示为 1)。
- 使用这个内部表示作为索引访问跳转表。
- 从跳转表中获取对应的代码地址并跳转执行。
这种实现方式的时间复杂度是 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 语句有以下主要区别:
-
穷尽性检查:Swift 要求 switch 语句必须覆盖所有可能的情况,除非提供了 default 分支。而 C 语言没有这个要求,可能会导致未处理的情况。
-
不允许隐式贯穿:在 Swift 中,执行完一个 case 分支后不会继续执行下一个 case 分支,不需要使用 break 语句。而在 C 语言中,默认情况下会发生执行穿透,需要显式使用 break 语句来避免。
-
模式匹配的多样性:Swift 的 switch 语句支持多种模式匹配,包括范围匹配、元组匹配、枚举关联值匹配、类型转换匹配等。而 C 语言的 switch 语句只能匹配整数类型(包括字符类型)。
-
where 子句:Swift 的 switch 语句支持 where 子句,可以进一步过滤匹配条件。而 C 语言没有这个特性。
-
类型安全性: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 语句有以下主要区别:
-
支持的类型:Swift 的 switch 语句可以匹配多种类型,包括整数、浮点数、字符串、枚举、元组、可选类型等。而 Java 的 switch 语句在 Java 7 之前只能匹配整数类型(包括字符类型),Java 7 及以后可以匹配字符串类型,但仍然不支持浮点数、元组等类型。
-
穷尽性检查:Swift 要求 switch 语句必须覆盖所有可能的情况,除非提供了 default 分支。而 Java 没有这个要求,可能会导致未处理的情况。
-
不允许隐式贯穿:在 Swift 中,执行完一个 case 分支后不会继续执行下一个 case 分支,不需要使用 break 语句。而在 Java 中,默认情况下会发生执行穿透,需要显式使用 break 语句来避免。
-
模式匹配:Swift 的 switch 语句支持强大的模式匹配,包括范围匹配、元组匹配、枚举关联值匹配、类型转换匹配等。而 Java 的 switch 语句只能进行简单的值匹配。
-
返回值: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 表达式有很多相似之处,因为它们都提供了强大的模式匹配功能。以下是它们的主要区别:
-
语法形式:Swift 使用 switch 关键字,而 Kotlin 使用 when 关键字。此外,Kotlin 的 when 可以作为表达式使用,也可以作为语句使用,而 Swift 的 switch 语句在 Swift 5.1 之前只能作为语句使用,Swift 5.1 及以后可以作为表达式使用。
-
默认行为:Swift 的 switch 语句默认不会发生执行穿透,而 Kotlin 的 when 表达式也不会发生执行穿透,不需要使用 break 语句。
-
穷尽性检查:Swift 和 Kotlin 都要求对枚举类型的匹配必须覆盖所有可能的情况,除非提供了 else 分支(Kotlin)或 default 分支(Swift)。
-
模式匹配:Swift 和 Kotlin 都支持多种模式匹配,包括值匹配、范围匹配、类型转换匹配等。但 Swift 的模式匹配更加灵活,例如支持元组模式匹配、枚举关联值匹配等。
-
与 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
3212

被折叠的 条评论
为什么被折叠?



