现在位置: 首页 > Swift 教程 > 正文

Swift 条件语句

条件语句根据一个或多个条件的真假,决定执行哪一段代码。

Swift 提供了 if、switch、guard 和三元运算符,它们的共同前提是条件必须是 Bool。


if 语句

if 语句由一个布尔条件和一段代码块组成,条件为 true 时才执行代码块。

实例

func checkIf(_ varA: Int) {
    if varA < 20 {
        print("varA 小于 20")
    }
    print("varA 变量的值为 \(varA)")
}

checkIf(10)

以上程序执行结果为:

varA 小于 20
varA 变量的值为 10

注意 if 后面的大括号不能省略,即使代码块里只有一行。


if-else 与 else if 链

if 后面可以跟一个 else,条件为 false 时执行 else 分支。

实例

func checkIfElse(_ varA: Int) {
    if varA < 20 {
        print("varA 小于 20")
    } else {
        print("varA 大于或等于 20")
    }
}

checkIfElse(100)

以上程序执行结果为:

varA 大于或等于 20

需要判断多个条件时,用 else if 串成一条链,一旦某个分支命中,后面的分支就都不会再执行。

实例

func checkChain(_ score: Int) {
    if score == 20 {
        print("score 的值为 20")
    } else if score == 50 {
        print("score 的值为 50")
    } else if score >= 80 {
        print("score 大于等于 80")
    } else {
        print("没有匹配条件")
    }
}

checkChain(85)

以上程序执行结果为:

score 大于等于 80

嵌套 if

if 或 else if 的代码块里可以再写 if,形成嵌套判断。

实例

func checkNested(_ n1: Int, _ n2: Int) {
    if n1 == 100 {
        print("第一个条件为 true")
        if n2 == 200 {
            print("第二个条件也是 true")
        }
    }
}

checkNested(100, 200)

以上程序执行结果为:

第一个条件为 true
第二个条件也是 true

嵌套层数太多会降低可读性,这时通常改用 guard 提前退出,或者把判断抽成独立的函数。


条件必须是 Bool

Swift 要求条件位置放一个真正的 Bool,整数、字符串、可选值都不能直接当条件。

实例

func checkBool(_ count: Int, flag: Bool) {
    if count > 0 {          // 用比较运算得到 Bool
        print("count 是正数")
    }
    if flag {               // flag 本身就是 Bool
        print("flag 为 true")
    }
}

checkBool(3, flag: true)

以上程序执行结果为:

count 是正数
flag 为 true

把整数直接当条件会编译失败。

实例

let count = 3
if count {
    print("count 非零")
}

编译器给出的错误是:

error: type 'Int' cannot be used as a boolean; test for '!= 0' instead

注意:Swift 没有 C 语言那种「0、空字符串、空指针为假」的隐式转换,也没有 not 关键字,取反要用 !。可选值想当条件用,必须先解包,写法见第 29 篇《Swift 可选类型》。


三元运算符

条件 ? 值1 : 值2 是 if-else 的表达式版本,适合在两个值之间二选一。

实例

func voteText(_ age: Int) -> String {
    return age >= 18 ? "可以投票" : "不能投票"
}

print(voteText(20))
print(voteText(15))

以上程序执行结果为:

可以投票
不能投票

三元表达式嵌套太多会难读,超过两层时建议改回 if-else。


switch 语句

switch 把一个值和若干 case 逐一比对,命中后执行对应分支。

和 C 语言不同,Swift 的 case 执行完会自动结束,不需要写 break,也不会掉进下一个 case。

区间匹配

case 里可以直接写区间,用来给分数、年龄这类连续值分档。

实例

func gradeText(_ grade: Int) -> String {
    switch grade {
    case 90...100:
        return "优秀"
    case 80..<90:
        return "良好"
    case 60..<80:
        return "及格"
    default:
        return "不及格"
    }
}

print(gradeText(85))

以上程序执行结果为:

良好

元组匹配

把多个值组合成元组,再用带模式的 case 一次性匹配多个维度,下划线 _ 表示这一位不关心。

实例

func axisText(_ coord: (Int, Int)) -> String {
    switch coord {
    case (0, 0):
        return "原点"
    case (_, 0):
        return "在 x 轴上"
    case (0, _):
        return "在 y 轴上"
    default:
        return "其他位置"
    }
}

print(axisText((0, 5)))

以上程序执行结果为:

在 y 轴上

值绑定与 where

用 case let (x, y) 可以把匹配到的值绑定到局部常量,再用 where 追加额外条件。

实例

func diagonalText(_ point: (Int, Int)) -> String {
    switch point {
    case let (x, y) where x == y:
        return "(\(x), \(y)) 在主对角线上"
    case let (x, y) where x == -y:
        return "(\(x), \(y)) 在反对角线上"
    case let (x, y):
        return "(\(x), \(y)) 是普通点"
    }
}

print(diagonalText((3, -3)))

以上程序执行结果为:

(3, -3) 在反对角线上

多值匹配与 default

一个 case 里可以列出多个用逗号分隔的值,命中任意一个即可。

实例

func indexText(_ index: Int) -> String {
    switch index {
    case 100:
        return "index 的值为 100"
    case 10, 15:
        return "index 的值为 10 或 15"
    case 5:
        return "index 的值为 5"
    default:
        return "默认 case"
    }
}

print(indexText(10))

以上程序执行结果为:

index 的值为 10 或 15

注意:只有当 switch 已经穷尽所有可能时才能省略 default。对 Int 这类取值无限的类型必须写 default;对 Bool 或已列出全部成员的枚举,则可以省略。

实例

// Bool 只有 true 和 false,两个 case 就穷尽了,不需要 default
func boolText(_ flag: Bool) -> String {
    switch flag {
    case true:
        return "真"
    case false:
        return "假"
    }
}

print(boolText(true), boolText(false))

以上程序执行结果为:

真 假

@unknown default

当 switch 的值来自另一个模块的枚举时,那个枚举未来可能新增成员,这时用 @unknown default 兜底。

实例

// 假设 direction 的类型来自另一个模块,且该枚举不是 frozen 的
switch direction {
case .north:
    print("北")
case .south:
    print("南")
@unknown default:
    print("未知方向")
}

上面这段代码无法单独运行,因为 direction 的类型需要由外部模块提供。

@unknown default 的作用是:新版本枚举增加成员时,编译器会给出警告提醒你去处理,而不是直接编译失败。

注意:如果枚举就定义在同一个文件里,编译器已经知道它的全部成员,此时写 @unknown default 并不能消掉「switch must be exhaustive」警告,仍然要把所有 case 列全,所以同一模块内直接用 default 即可。


fallthrough

默认情况下,Swift 的 case 执行完就结束;如果确实想继续执行下一个 case 的代码,要显式写 fallthrough。

实例

func fallthroughDemo(_ idx: Int) {
    switch idx {
    case 100:
        print("index 的值为 100")
    case 10, 15:
        print("index 的值为 10 或 15")
        fallthrough
    case 5:
        print("index 的值为 5")
    default:
        print("默认 case")
    }
}

fallthroughDemo(10)

以上程序执行结果为:

index 的值为 10 或 15
index 的值为 5

注意:fallthrough 不会再做任何条件判断,直接进入下一个 case 的代码块。它只能掉进普通的 case;如果下一个 case 需要绑定变量,编译器会报 'fallthrough' from a case which doesn't bind variable。


guard 提前退出

guard 用来在条件不满足时提前离开当前作用域,它的 else 分支必须跳出(return、break、continue 或 throw)。

把不满足的情况放在前面挡掉,主逻辑就能少一层缩进,读起来更顺。

实例

func checkAge(_ age: Int) -> String {
    guard age >= 18 else {
        return "未成年,不能进入"
    }
    return "已成年,欢迎进入"
}

print(checkAge(20))
print(checkAge(15))

以上程序执行结果为:

已成年,欢迎进入
未成年,不能进入

guard let 是它最常用的形态:解包可选值,失败就退出,成功则解包后的常量在后续代码中一直可用。

实例

func greet(_ name: String?) -> String {
    guard let name = name, !name.isEmpty else {
        return "你好,游客"
    }
    // 到这里 name 已经是非空的 String
    return "你好,\(name)"
}

print(greet("Runoob"))
print(greet(nil))

以上程序执行结果为:

你好,Runoob
你好,游客

常见问题

下面几个问题在写条件语句时最常遇到。

问题说明
if 1 { } 为什么报错条件必须是 Bool,改成 if 1 != 0 { }
switch 的 default 能省略吗穷尽时可以;Int 这类必须写
case 后面要写 break 吗不用,Swift 默认不会往下掉
guard 和 if 怎么选需要提前退出、减少缩进时用 guard;只是分支选择时用 if
guard 里能声明变量吗可以,但声明的变量在 guard 之后依然可用,这是它和 if let 的主要区别