Swift 控制流深度解析(一):循环、条件与分支

为什么 Swift 的控制流值得单开一篇?

  1. 语法糖多:区间、stride、tuple、where、guard、defer......
  2. 安全严苛:switch 必须 exhaustive、case 不能空、默认不贯穿。
  3. 表达能力强:if/switch 可以当表达式用,一行赋值即可。
  4. 场景丰富:从日常循环到资源清理、API 可用性检查,全覆盖。

For-In 循环:从"会写"到"写对"

基本形态

swift 复制代码
// 遍历数组
let fruits = ["apple", "orange", "banana"]
for fruit in fruits {
    print("我喜欢吃\(fruit)")
}

字典遍历注意顺序

swift 复制代码
let legCount = ["ant": 6, "snake": 0, "cat": 4]
// 字典无序!同一台机器多次运行顺序都可能不同
for (animal, legs) in legCount {
    print("\(animal) 有 \(legs) 条腿")
}

区间与"忽略值"

swift 复制代码
// 闭区间 ...  包含两端
for i in 1...5 {
    print("5 x \(i) = \(5 * i)")
}

// 半开区间 ..<  忽略最后一项
let minutes = 0..<60          // 0~59
for tick in minutes where tick % 5 == 0 {
    // 只打印 0/5/10/.../55
    print("表盘刻度 \(tick)")
}

// 如果根本不想用下标,用 _ 占位
var base = 1
for _ in 1...10 {            // 3 的 10 次方
    base *= 3
}
print("3^10 = \(base)")     // 59049

stride 灵活跳步

swift 复制代码
// 开区间 stride(from:to:by:)
for degree in stride(from: 0, to: 360, by: 30) {
    print("旋转 \(degree)°")
}

// 闭区间 stride(from:through:by:)
for rate in stride(from: 0.5, through: 1.5, by: 0.25) {
    print("汇率 \(rate)")
}

While 与 Repeat-While:何时选谁?

场景 推荐
可能一次都不执行 while
至少执行一次 repeat-while

蛇梯棋:同一逻辑两种写法

swift 复制代码
let finalSquare = 25
var board = Array(repeating: 0, count: finalSquare + 1)
// 梯子
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
// 蛇
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08

// ----- while 版 -----
var square = 0, diceRoll = 0
while square < finalSquare {
    diceRoll += 1
    if diceRoll == 7 { diceRoll = 1 }          // 模拟 1~6 骰子
    square += diceRoll
    if square < board.count { square += board[square] }
}
print("while 版到达终点")

// ----- repeat-while 版 -----
square = 0; diceRoll = 0
repeat {
    square += board[square]   // 先结算梯子/蛇
    diceRoll += 1
    if diceRoll == 7 { diceRoll = 1 }
    square += diceRoll
} while square < finalSquare
print("repeat-while 版到达终点")

经验:

  • 必须先"爬梯子/滑蛇"再"掷骰子"时,用 repeat-while 可以省一次越界检查。
  • 其余情况 while 可读性更高。

条件语句:if / guard / switch 全维度对比

if 表达式(Swift 5.9+)

swift 复制代码
let temp = 26
// 一行赋值,不再需要三目嵌套
let advice = if temp <= 0 { "穿羽绒服" }
             else if temp >= 30 { "短袖+冷饮" }
             else { "正常穿衣" }
print(advice)   // 正常穿衣

注意:

  • 所有分支必须返回同一类型,否则需要显式标注类型。
  • 可以抛错:let level = if temp > 100 { throw TempError.boiling } else { "ok" }

guard:提前退出,减少金字塔

swift 复制代码
func buy(age: Int, stock: Int) {
    guard age >= 18 else {
        print("未成年禁止购买")
        return
    }
    guard stock > 0 else {
        print("库存不足")
        return
    }
    // 以下代码一定是成年人且有库存
    print("购买成功")
}

guard 与 if 的区别:

  1. 必须带 else
  2. else 内必须中断控制流(return/break/continue/throw/fatalError);
  3. 解绑变量作用域延续到后续代码,避免多层嵌套。

switch:模式匹配大杀器

区间 & 复合值

swift 复制代码
let score = 87
switch score {
case 90...100: print("优秀")
case 80..<90:  print("良好")
case 60..<80:  print("及格")
default:       print("不及格")
}

tuple + where 条件

swift 复制代码
let point = (2, 2)
switch point {
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) where x == y:  print("在对角线 x=y 上")
default:                      print("其他")
}

值绑定与复合 case

swift 复制代码
// 复合 case 共享同一段代码
switch "e" {
case "a", "e", "i", "o", "u": print("小写元音")
case "b", "c", "d", "f", "g": print("部分辅音")
default: print("其他字符")
}

// 复合 case 也支持绑定,但要保证类型一致
switch (2, 0) {
case (let d, 0), (0, let d):   // 两个 pattern 都绑定 d 且类型一致
    print("到轴距离为 \(d)")
default:
    break
}

贯穿:显式 fallthrough

swift 复制代码
let integerToDescribe = 5
var description = "数字 \(integerToDescribe) 是"
switch integerToDescribe {
case 2, 3, 5, 7, 11, 13, 17, 19:
    description += " 质数,且"
    fallthrough          // 继续执行下一个 case
default:
    description += " 是整数。"
}
print(description)   // 数字 5 是 质数,且 是整数。

控制转移语句:continue / break / fallthrough / return / throw

continue & break 的最常见误区

swift 复制代码
// 去掉小写元音与空格
let puzzleInput = "great minds think alike"
let vowels: Set<Character> = ["a", "e", "i", "o", "u", " "]
var output = ""
for ch in puzzleInput {
    if vowels.contains(ch) {
        continue          // 立即进入下一轮
    }
    output.append(ch)
}
print(output)   // grtmndsthnklk

带标签的语句:精确跳出血腥嵌套

swift 复制代码
var finalSquare = 25
var square = 0
gameLoop: while true {
    let dice = Int.random(in: 1...6)
    switch square + dice {
    case finalSquare:
        print("刚好到达,游戏胜利")
        break gameLoop      // 跳出 while,不是跳出 switch
    case let n where n > finalSquare:
        print("点数太大,重新掷")
        continue gameLoop   // 继续 while 下一轮
    default:
        square += dice
    }
}

defer:作用域退出时的"扫尾"利器

swift 复制代码
func updateScore() {
    var score = 10
    if Bool.random() {
        score += 5
        defer { print("本次加分已落地,当前分数:\(score)") }  // 1️⃣ 先写后执行
        defer { score -= 100 }                                // 2️⃣ 再写先执行
        print("离开 if 前 score=\(score)")                      // 15
    }
    print("离开函数前 score=\(score)")                          // -85
}
updateScore()

规则小结:

  • 同一作用域多个 defer 以栈顺序执行(先注册后执行)。
  • 无论正常 return、break、continue、throw 都会执行;进程崩溃除外。
  • 典型场景:文件句柄/锁/数据库事务配对释放。

API 可用性检查:让旧系统安心升级

swift 复制代码
if #available(iOS 15, macOS 12, *) {
    // 仅 iOS15+/macOS12+ 能走到这里
    print("iOS15+/macOS12+")
} else {
    // 低版本走兼容方案
    print("iOS15以下或者macOS12以下或者其他系统,比如Linex、visionOS")
}

// guard 写法,提前 return
func useNewAPI() {
    guard #available(macOS 12, *) else { return }
    // 以下代码编译器保证只在 macOS12+ 运行
}

总结

控制流元素 关键记忆点
for-in 可遍历任何 Sequence;字典无序;stride 可跳步
while vs repeat-while 是否"先检查"
if 表达式 同类型、可抛错、可函数返回值
guard 必须 else 中断,解绑变量作用域外可用
switch exhaustive、默认不贯穿、支持 tuple/where/值绑定
defer 栈式延迟,资源清理黄金搭档
#available 编译期+运行期双重保险

扩展思考:把知识迁移到日常开发

  1. 日志场景

    defer 在函数出口统一打印耗时,避免早期 return 漏埋点。

  2. 资源池

    文件句柄获取后立刻写 defer { fclose(fp) },再也不怕忘记关文件。

  3. 交互式 UI

    利用 stride(from: 0, to: 360, by: 30) 生成圆形按钮坐标,一行代码搞定数学。

  4. 数据校验

    多层 guard 提前返回,把"非法输入"挡在门外,主流程保持一级缩进。

控制流不是"写得出",而是"写得对、写得优雅"。

相关推荐
东坡肘子5 小时前
惊险但幸运,两次!| 肘子的 Swift 周报 #0109
人工智能·swiftui·swift
胖虎15 小时前
Swift项目生成Framework流程以及与OC的区别
framework·swift·1024程序员节·swift framework
songgeb21 小时前
What Auto Layout Doesn’t Allow
swift
YGGP1 天前
【Swift】LeetCode 240.搜索二维矩阵 II
swift
YGGP2 天前
【Swift】LeetCode 73. 矩阵置零
swift
非专业程序员Ping3 天前
HarfBuzz 实战:五大核心API 实例详解【附iOS/Swift实战示例】
android·ios·swift
Swift社区4 天前
LeetCode 409 - 最长回文串 | Swift 实战题解
算法·leetcode·swift
YGGP6 天前
【Swift】LeetCode 54. 螺旋矩阵
swift
Swift社区6 天前
Foundation Model 在 Swift 中的类型安全生成实践
开发语言·安全·swift
HarderCoder6 天前
【Swift 可选链】从“如果存在就点下去”到“安全穿隧”到空合运算符
swift