Swift 流程控制完全指南:if、guard、switch 与循环详解

发布时间:2026/10/12 16:17:39

Swift 流程控制完全指南:if、guard、switch 与循环详解 都说流程控制是编程语言里的“交通规则”到了 Swift 这里你会发现这套交通规则画得格外用心。它的 if、for、while 这些关键字看着和 C 语言沾亲带故写起来却处处有新意switch 不写 break 也不会贯穿、case 可以匹配整个区间、guard 天天劝你提前 return。这篇是我的 Swift 学习笔记第二篇专门把流程控制彻底捋一遍从最基础的 if-else 到模式匹配级别的 switch再到嵌套循环里的标签跳转每个语法点都配实测过的代码。适合正在啃 Swift 语法的新人也适合从 Python 或 JavaScript 转过来、想快速摸清 Swift 脾气的开发者。看完笔记再自己动手敲一遍流程控制这块基本就焊死了。1. 先建立整体认知Swift 流程控制的三大块与设计气质1.1 三大块条件分支、循环结构、控制转移Swift 的流程控制大致可以分成三条主线条件分支、循环结构、控制转移。条件分支管的是“根据情况走哪条路”if、guard、switch 都属于这一类循环结构管的是“重复做某件事直到条件不满足”for-in、while、repeat-while 都在此列控制转移则是穿插在分支和循环里的小机关break、continue、fallthrough、return、throw 负责在合适的时机打断流程、跳向别处。这套划分听起来和大多数语言没有本质区别但 Swift 在实现上有一个贯穿始终的原则让编译器帮你挡住错误。很多在其他语言里要靠自觉才能避免的坑在 Swift 里直接编译不过。最典型的例子就是 switch 的每个 case 执行完自动退出想穿透还得专门写 fallthrough直接消灭了“忘了写 break 导致逻辑穿透”这类经典 bugguard 的 else 分支必须 return 或 throw否则编译器报错这种强制约束让“忘了提前退出”的疏漏根本活不到运行时。这一节先给整体框架后面几节逐个拆开讲。如果你现在脑子里是“流程控制嘛我早就会了”的状态建议也重点看看 switch 那一节Swift 的 switch 和你在别的语言里见过的 switch很可能不是同一个量级的东西。1.2 和 C、Python、JS 对比三个最容易让人意外的地方先说三个你大概率会愣一下的细节都是跨语言转过来的高频困惑点。第一if 和 while 的条件括号可以不写但大括号不能省。写if (x 5) { }不报错写if x 5 { }也合法可你要是学 C 语言的习惯条件后面只跟一条语句、省略大括号编译直接失败。这种设计去掉了代码里最没意义的视觉噪音同时保留了块结构的明确性。第二多条件判断可以用逗号连接不用写一长串。这不是语法糖那么简单而是把条件清单的语义摆到了台面上后面实战部分会专门演示。第三也是最能体现 Swift 特色的一点switch 的 case 后面能写区间、元组、绑定变量、where 条件甚至可以从一堆异构值里精准筛出某种类型。这一下把 switch 从“整数分发器”升级成了完整的模式匹配工具很多在其他语言里要堆一摞 if-else 的逻辑在 Swift 里一个 switch 就安排得明明白白。这三条先记住后面你会在多个小节里反复见到它们的身影。2. 条件分支实战if、if let、guard 的选型逻辑2.1 if/else 基础写法与多条件逗号连接if 的最基本形态长这样let score 86 if score 90 { print(优秀) } else if score 60 { print(及格) } else { print(继续加油) }这几乎是所有类 C 语言的同款长相只有两个小差异条件表达式外面的括号可写可不写我个人建议平时不写保持 Swift 代码的简约风格大括号绝对省略不了哪怕分支体只有一行。另外注意Swift 里没有独立的elseif关键字你看到的else if是整体写法多个分支按顺序互斥判断命中一个就不会进另一个。多条件判断时Swift 提供了逗号写法比一串可读性强不少let age 20 let hasTicket true if age 18, hasTicket { print(请进) } else { print(不能进) }这里的逗号等价于但语义上更像“这几个条件依次满足”。实际工作中我发现条件列表超过两个时逗号写法比写一长串清晰得多尤其是每个条件需要单独换行时代码就像在列清单。记住一个关键点逗号写法里也可以混入可选绑定这是下一节的重点。2.2 可选绑定if let、if var 与 Swift 5.7 的简写Swift 里可选类型无处不在方法返回一个Int?你要可靠的下一步往往是“里面真的有值”。可选绑定就是把这层 Optional 壳拆开、把里面的值取出来用的标准手段let maybeNumber Int(123) if let number maybeNumber { print(转换成功\(number)) } else { print(转换失败maybeNumber 为 nil) }if let number maybeNumber做的事是如果maybeNumber有值就解包并把值赋给常量number进入第一个分支如果它是 nil就走进 else。需要注意number只在 if 代码块内有效出了这个块就找不到了。多个 Optional 要同时解包用逗号连起来甚至可以和普通条件混写let firstName: String? 张 let lastName: String? 三 if let firstName firstName, let lastName lastName, !firstName.isEmpty { print(\(lastName)\(firstName)) }三个条件按顺序判断两个解包全部成功并且名字非空才会走到最后那行打印。中间任意一个 Optional 是 nil整个条件就是 false直接进 else。这种“解包 条件过滤”一次完成的能力比先解包再单独判断要少写不少嵌套。从 Swift 5.7 开始官方提供了简写当解包出来的变量名和原变量名一致时可以只写一次if let name { print(名字是 \(name)) }这等价于if let name name。我第一次看到时觉得跟没写一样习惯之后确实省手。但有两点必须提醒简写变量会遮蔽外层同名 Optional在代码块内你再想访问“原来那个 Optional”就得换个思路对新手来说前几十次最好老老实实写全量形式先理解解包和遮蔽的本质再切换到简写。如果解包后需要修改值用if var name拿到的是可变变量。2.3 guard 到底好在哪提前退出guard 是我在 Swift 里最依赖的流程控制之一核心设计就四个字提前退出。一个典型的登录校验函数长这样func validateUser(name: String?, password: String?) - Bool { guard let name name, !name.isEmpty else { return false } guard let password password, password.count 6 else { return false } print(校验通过\(name)) return true }仔细看两个 guard 把不合法的情况在函数一开始就拦掉了后面继续写逻辑的时候name和password已经是确定可用的非 Optional 值不需要再担心 nil。这就是 guard 和 if let 最大的区别if let 解包出来的变量作用域是那个代码块内部一出门就失效guard 解包出来的变量作用域是整个函数后续部分因为 guard 保证了解包失败时函数已经提前返回根本走不到后面。几个细节务必要记住。第一guard 必须搭配 elseelse 里必须用 return、throw、break、continue 或 fatalError 这类方式离开当前作用域否则编译报错。第二不要把 guard 当 if 用如果解包结果只在这一处用后面不再需要就没必要上 guard。第三guard 连写多个判断时条件是从上到下顺序执行的把最可能失败、判断成本最低的条件放前面可以省掉不少无谓计算。3. switch 进阶模式匹配才是这门语言的拿手好戏3.1 自动 break、必须穷举与 fallthroughSwift 的 switch 和 C 语言最大的分水岭就在 break 这里。C 语言里每个 case 结尾不写 break程序就会悄悄穿入下一个 case生产环境里由它贡献的血泪 bug 数都数不完。Swift 直接反转了这种行为case 执行完自动退出不贯穿你真的需要贯穿时用 fallthrough 显式声明let number 2 switch number { case 1: print(一) case 2: print(二) fallthrough case 3: print(三) default: print(其它) }这段代码虽然匹配的是 case 2但 fallthrough 会让它继续执行 case 3 的代码控制台会依次输出“二”和“三”。三个细节值得注意fallthrough 只能往后穿一层不会自己接着往下穿到底fallthrough 不能穿到一个带值绑定的 case 里编译器会提示这种转移在值绑定上下文里不受支持fallthrough 完全无视下一个 case 的条件匹配说白了它是一次无条件跳转。switch 另一个硬性要求是穷举编译器要求 switch 覆盖所有可能情况。如果你 switch 一个 Int写了几个 case 但没写 default编译器直接报错switch must be exhaustive。为什么这么严格因为穷举可以把“漏掉一种分支”的运行时风险提前拉到编译期。对 enum 类型来说尤其舒服enum 的 case 就那几个编译器知道你已经全部覆盖了连 default 都不用写。3.2 区间、元组、值绑定与通配符的组合匹配case 后面的表达式不一定是单一常量它可以是一个区间、一个元组、一个绑定变量的模式甚至是一个下划线通配符。我把最实用的组合一次性列出来func describe(_ point: (Int, Int)) { switch point { case (0, 0): print(原点) case (_, 0): print(在 x 轴上) case (0, _): print(在 y 轴上) case (-2...2, -2...2): print(在原点附近的小方框里) case let (x, y) where x y: print(在对角线上x y \(x)) default: print(其他位置\(point)) } }每行 case 都在做不同种类的事(0, 0)是精确匹配(_, 0)用通配符忽略 x 坐标只看 y(-2...2, -2...2)是区间匹配最后一行case let (x, y) where x y是值绑定加 where 条件。这一个函数体现出来的匹配能力放在 C 语言里得写四五个 if-else 才能磕磕绊绊达到同样效果。值绑定是这里最有用的设计case 匹配时可以把匹配到的值顺手绑定成一个常量在代码块里直接用。处理网络响应的场景就很典型let result (code: 404, message: Not Found) switch result { case (200, _): print(成功) case (let code, let message) where code 400 code 500: print(客户端错误 \(code)\(message)) default: print(其他) }用case let (code, message)把元组的两个分量都绑出来再通过 where 过滤一段逻辑清晰得不像话。这种写法在处理坐标计算、错误枚举、状态机转换时真的是救星级体验。3.3 where 过滤、if case 与 switch 表达式where 关键字在 switch 里是模式匹配的增强器。switch 已经从值匹配升级到模式匹配了where 就是给匹配加一道动态过滤闸门let age 24 switch age { case let a where a 18: print(未成年人) case let a where a 60: print(成年人\(a)岁) default: print(老年人) }case let a把原始值接管过来然后用 where 判断 a 的范围。这种写法的优势在于条件可以非常复杂比如case let a where a % 2 0 a 10在别的语言里又要展开一串 if-else。除了 switch 语句本身Swift 还支持把模式匹配用在 if 里也就是if case。它本质上把一个 switch 的单个 case 拿出来当条件用let coordinate (10, 0) if case (_, 0) coordinate { print(在 x 轴上) }配合 for 还能做条件遍历for case直接跳过不匹配的元素let values: [Any] [1, two, 3.0, 四, 5] for case let text as String in values { print(字符串\(text)) }这段代码遍历数组时只挑出 String 类型的元素其他类型自动跳过输出“two”和“四”。这就是模式匹配嵌进循环的威力。类似地还有if case let value? optionalValue这种专门处理 Optional 的模式在协议解析和数据清洗里非常常见。最后提一个 Swift 5.9 开始支持的新写法switch 表达式。switch 可以出现在赋值符号右边直接产出一个值let score 87 let level switch score { case 90...100: 优秀 case 60..90: 良好 case 0..60: 需要努力 default: 非法分数 } print(等级\(level))每个分支的返回值就是整个表达式的值不需要再在每个 case 里写赋值语句。这种写法在“根据状态计算配置”这类场景里可读性非常高。注意每个分支必须返回相同类型的值否则编译器不答应。4. 循环结构完全指南for-in、while 与 repeat-while4.1 for-in 能遍历什么数组、字典、区间、stridefor-in 是 Swift 里使用频率最高的循环没有之一。它的基本写法是从一个序列里依次取出元素let fruits [苹果, 香蕉, 橙子] for fruit in fruits { print(fruit) } for (index, fruit) in fruits.enumerated() { print(\(index)\(fruit)) } let scores [语文: 90, 数学: 85, 英语: 92] for (subject, score) in scores { print(\(subject) \(score) 分) }数组、字典都能直接进 for-in。字典的遍历顺序不保证每次打印的顺序可能不同这是哈希结构的本性。如果需要稳定顺序先排序再遍历for subject in scores.keys.sorted() { print(\(subject)\(scores[subject] ?? 0) 分) }区间是 for-in 的另一大搭档。闭区间...包含终点半开区间..不包含终点for i in 1...5 { print(i) // 1 2 3 4 5 } for i in 0..5 { print(i) // 0 1 2 3 4 }如果循环不需要用到索引本身只在乎次数下划线是常规写法for _ in 0..3 { print(重复三次) }想按固定步长跳跃遍历用 stride 函数for i in stride(from: 0, to: 10, by: 2) { print(i) // 0 2 4 6 8 } for i in stride(from: 10, through: 1, by: -1) { print(i) // 10 9 8 7 6 5 4 3 2 1 }to是“到但不过终点”through是“一路穿过终点”边界语义要分清楚。默认的1...5只能递增想倒着遍历要么用 stride要么先用 reversed()。新手常见的困惑是在0..5上调用 reversed() 后发现类型变成了 ReversedCollection其实直接 for 遍历完全没问题Swift 在底层已经处理好了。字符串也能直接遍历每个字符就是 Character 类型处理字词统计时很顺手。4.2 while 与 repeat-while先判后做与先做后判while 和 repeat-while 是一对兄弟区别只在判断时机上var count 0 while count 3 { print(当前 \(count)) count 1 } var n 5 repeat { print(剩余 \(n)) n - 1 } while n 0while 是“先判断条件满足了才进循环体”如果一开始条件就不成立循环体一次也不会执行。repeat-while 是“先执行一次循环体再判断条件”所以循环体至少执行一次。这在“至少要尝试一次”的场景里很有用比如先打印一次提示再判断要不要继续。有一类细节很值得警惕repeat-while 的条件在循环体之后才判断所以条件里使用的变量必须在循环体内被更新过否则极易写出死循环。比如上面例子要是忘了写n - 1循环就永远跳不出来。我写循环的时候习惯先在脑子里问一句这个循环靠什么终止答不上来就停下来检查。这个习惯帮我省下了数不清的调试时间。4.3 标签语句嵌套循环里的精准跳转break 和 continue 默认只作用于它们所在的那一层循环。嵌套两层以上循环时想在里层直接跳出外层就得给循环打标签outerLoop: for i in 1...3 { for j in 1...3 { if j 2 { continue outerLoop } print((\(i), \(j))) } }这里当 j 等于 2 时continue 不再只跳过内层循环的当前迭代而是直接进入外层循环的下一轮。打印结果是 (1,1)、(2,1)、(3,1)内层的第二次迭代每次都被跳过并且整个内层循环后续的迭代也不再执行。标签语句在“搜索矩阵找到目标就全部停止”的场景里特别好用let matrix [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ] searchLoop: for row in matrix { for value in row { if value 5 { print(找到 5) break searchLoop } } }break searchLoop一口气跳出两层循环比用标志位一层层 if 判断干净得多。标签的写法是在循环语句前加上名字:命名规范用驼峰。这个语法平时用得不多但它是“嵌套循环需要精准跳转”场景下的正牌解药。5. 常见问题与排查记录5.1 我在实战中踩过的坑第一个坑是 switch 穷举。刚开始写 Swift 时我总是习惯性地只写自己关心的 case不写 default结果编译器一直报错当时还觉得烦“我就想处理两个值其他情况谁管它。”后来理解了穷举设计的意义用在 enum 身上它能强迫你面对所有可能性。比如处理网络状态枚举时你写了 success 和 failure编译器还会提醒你是不是漏了 loading这类提醒真的能拦住不少漏网之鱼。第二个坑是 fallthrough 与值绑定冲突。有一回我试图在 case 1 里 fallthrough 到一个case let value的分支编译器直接拒绝。原因是 fallthrough 无条件进入下一个 case但值绑定 case 需要从匹配值中提取变量两者逻辑上天然矛盾。习惯之后我基本不再写 fallthrough 了需要贯穿的场合极少真遇到了更推荐重构逻辑或者用 if-else 替代。第三个坑是可选绑定里的变量遮蔽。早期用简写if let number时我在 if 块里访问 number发现类型是 Int 而不是原来的 Int?一瞬间有点懵。Swift 的规则是解包出来的常量会遮蔽外层同名变量在代码块内你拿到的就是解包后的值这其实是好事但别指望还能在同一作用域里同时访问 Optional 原始值。5.2 一份快速自查清单如果编译不过或者运行结果不对按这个单子过一遍大概率能解决问题。第一switch 漏了 default 吗Int、String 等类型必须补齐 default 分支enum 类型则看覆盖情况。第二guard 的 else 分支里正确 return 或 throw 了吗编译器会明确报错但经常有朋友把 guard 当 if 用忘了 else 里的退出语句。第三if let 解包出来的变量是不是在代码块外就被访问了作用域不对。第四repeat-while 循环体里有没有更新条件变量这是死循环最常见的成因。第五遍历字典时是不是默认了顺序结果不稳定就加 sorted()。第六区间边界选对了吗1...3包含 31..3不包含 3倒序遍历用 stride。5.3 给同样在学 Swift 的朋友几句建议流程控制这章是 Swift 的语法地基地基打牢了后面写函数、写类、写 SwiftUI 都会顺手很多。我的建议是不要只看不练。把 Playground 打开依次把这些场景写一遍给分数分等级、用循环打印九九乘法表、写一个 stride 倒计时、用 switch 做星座判断、嵌套循环加标签找出矩阵里的目标数。这几个例子每个都不超过二十行但能把 if、guard、switch、for-in、while、标签语句全过一遍。写完这些再去看那些堆着各种语法糖的项目源码心里就有底了。语法只是工具关键是脑子里建立起“这种场景该用哪种结构”的肌肉记忆。最后说一点我现在才体会到的东西流程控制看着是编程的入门内容但不同语言在这里的选择其实暴露了各自的设计哲学。Swift 在流程控制上走的路线是“把常见错误变成编译错误”“switch 穷举、guard 强制退出、case 自动中断全是在帮你把 Bug 拦在编译之前。这种设计一开始会觉得它管得宽写久了反倒离不开。我的下一篇笔记计划整理集合类型数组、字典、Set还有它们和流程控制的组合玩法。这篇里的每段代码都建议亲手敲一遍敲完再到项目里用收获会比单纯翻文档大得多。
延伸阅读

更多相关文章

2026/10/12 16:17:39

Hashtable扩容为什么是2倍+1?素数容量与哈希分布深度解析

记得有次跟朋友聊哈希表,他突然问了一句:Hashtable扩容为什么是2倍1,而不是直接2倍?我当时愣了一下,因为这问题表面上问的是一个扩容公式,实际牵扯到的却是哈希表设计里最核心的三件事:容量选择…

2026/10/12 16:17:39

驱动版本升级指南:从16.1656到16.1692的完整流程与避坑实践

1. 驱动版本升级这件事,为什么值得单独拿出来说驱动这东西,平时没人惦记它,一旦出问题就是大问题。我做硬件适配和系统调优这些年,见过太多人把驱动当成"装完就不管"的一次性消耗品,结果机器跑着跑着开始蓝屏…

2026/10/12 16:17:39

用Python对接Twilio实现短信通知系统:从API集成到实战调试

做业务系统时间长了你会发现,真正能在关键时刻把信息送到用户手里的通知渠道,短信依然是最稳的那一个。不管是服务器报警、订单状态变更、验证码下发,还是预约提醒,短信的穿透力和到达率是很多即时通讯工具比不了的。我自己在做监…

2026/10/13 0:59:44

400G多模光模块标准详解:SR8与SR4.2架构、链路预算及避坑指南

简介:IEEE Std 802.3cm-2020是IEEE发布的以太网标准修正案,面向光通信产品开发与网络系统设计者,解决400Gbps以太网在多模光纤上的物理层规范与管理参数问题。该标准基于IEEE Std 802.3-2018及多个相关修正案,新增400GBASE-SR8与4…

2026/10/13 0:59:44

多段线简化算法实战:RDP与VW选型及工程避坑指南

我第一次被多段线简化算法逼到墙角,是在处理一条水域边界线的时候。数据源导出了八千多个顶点,画到地图上直接变成一团黑色煤球,每一段岸边弯道都被采样得密密麻麻。那时候我抱着“减几个点嘛”的想法,直接用间隔抽点,…

2026/10/13 0:59:44

FastMCP 2.x 干货笔记:把 OpenAI API 接入 AI SDK 的 TaoToken 配置指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 0:02:13

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/12 12:01:26

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/12 12:54:07

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/13 0:04:41

基于Triton部署YOLO目标检测:从训练脚本到线上服务

简介:这份资源面向希望将目标检测模型落地到生产环境的深度学习开发者与算法工程师,聚焦于用 Triton Inference Server 部署 YOLOv9 的完整实战方案,解决从训练权重到线上推理服务之间的工程化难题。压缩包共 16 个文件,约 886KB&…

2026/10/13 0:04:41

手把手教你云端部署Qwen-Image-2.1:从GPU选型到在线文生图服务

Qwen-Image-2.1 这个开源的图像生成模型,最近在内容创作和 AI 应用圈子里热度相当高。它属于通义 Qwen 系列里的视觉生成分支,让我眼前一亮的是它对中文长文本的理解能力——以前用那些老方案生成带中文文字的图,十个里九个是“乱码印刷体”&…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑