Swift提供了各种控制流程语句。这些包括while循环多次执行任务;if, guard, and switch语句以基于特定条件执行不同的代码分支;以及诸如break and continue将执行流程转移到代码中的另一个点。
Swift还提供了一个for-in循环,可以轻松遍历数组,字典,范围,字符串和其他序列。
Swift的switch语句比许多C语言中的对应语言强大得多。个案可以匹配许多不同的模式,包括区间匹配,元组和特定类型的强制转换。匹配的开关情况下的值可以绑定到临时常量或变量,以便在案例的主体中使用,并且复杂的匹配条件可以用每个案例的where子句表示。
For-In循环
您可以使用for-in循环遍历序列,例如数组中的项目,数字范围或字符串中的字符。
此示例使用for-in循环遍历数组中的项目:
let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names {
print("Hello, \(name)!")
}
// Hello, Anna!
// Hello, Alex!
// Hello, Brian!
// Hello, Jack!
您也可以迭代字典来访问其键值对。 当字典迭代时,字典中的每一项都作为(键,值)元组返回,您可以将(键,值)元组的成员分解为明确命名的常量,以便在for-in循环的主体中使用。 在下面的代码示例中,字典的键被分解为一个名为animalName的常量,字典的值被分解为一个称为legCount的常量。
let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
for (animalName, legCount) in numberOfLegs {
print("\(animalName)s have \(legCount) legs")
}
// ants have 6 legs
// spiders have 8 legs
// cats have 4 legs
字典的内容本身是无序的,迭代它们并不能保证它们将被检索的顺序。 特别是,将项目插入词典的顺序不会定义它们被迭代的顺序。 有关数组和字典的更多信息,请参阅集合类型。
您也可以使用数字范围的for-in循环。 此示例在五次表中打印前几个条目:
for index in1...5 {
print("\(index) times 5 is \(index * 5)")
}
// 1 times 5 is 5
// 2 times 5 is 10
// 3 times 5 is 15
// 4 times 5 is 20
// 5 times 5 is 25
正在迭代的序列的范围是从1到5(包括1和5),如使用闭范围运算符(...)所示。索引的值设置为范围(1)中的第一个数字,并执行循环内的语句。在这种情况下,循环仅包含一条语句,该语句从索引当前值的五次表中打印条目。语句执行后,索引的值更新为包含范围(2)中的第二个值,并再次调用print(_:separator:terminator :)函数。这个过程一直持续到范围的结束。
在上面的例子中,index是一个常量,其值在循环的每次迭*开代**始时自动设置。因此,索引在使用之前不必声明。它只是简单地通过包含在循环声明中来隐式声明,而不需要let声明关键字。
如果您不需要序列中的每个值,则可以通过使用下划线代替变量名称来忽略这些值。
let base = 3
let power = 10
var answer = 1
for _ in 1... power {
answer *= base
}
print("\(base) to the power of \(power) is \(answer)")
// Prints "3 to the power of 10 is 59049"
上面的例子计算一个数字与另一个数字的值(在这种情况下,3的幂为10)。 它使用以1开始并以10结尾的闭合范围,将起始值1(即3的0次幂)乘以3,十次。对于此计算,每次通过循环的单个计数器值 是不必要的 - 代码只需执行正确的循环次数。 用于代替循环变量的下划线字符(_)会导致忽略单个值,并且在循环的每次迭代过程中不提供对当前值的访问。
在某些情况下,您可能不想使用封闭范围,其中包括两个端点。 考虑在表盘上每刻绘制刻度标记。 您想从0分钟开始绘制60个刻度线。 使用半开范围运算符(.. <)来包含下界但不包含上界。 有关范围的更多信息,请参阅范围运算符。
let minutes = 60
for tickMark in 0..<minutes {
// render the tick mark each minute (60 times)
}
有些用户可能需要在他们的用户界面中减少刻度线 他们可以每5分钟更换一个标记。 使用stride(from:to:by:)函数跳过不需要的标记。
let minuteInterval = 5
for tickMark in stride(from: 0, to: minutes, by: minuteInterval) {
// render the tick mark every 5 minutes (0, 5, 10, 15 ... 45, 50, 55)
}
封闭范围也是可用的,通过使用stride(from:through:by:):
let hours = 12
let hourInterval = 3
for tickMark in stride(from: 3, through: hours, by: hourInterval) {
// render the tick mark every 3 hours (3, 6, 9, 12)
}
While循环
while循环执行一组语句,直到条件变为false。 当迭代次数在第一次迭*开代**始之前未知时,最好使用这些类型的循环。 Swift提供了两种while循环:
While 每次通过循环开始时评估其状态。
Repeat-while 每次循环结束时评估其状态。
while循环从评估单个条件开始。 如果条件为真,则重复一组语句直到条件变为假。
以下是while循环的一般形式:
-
whilecondition {
-
statements
-
}
-
这个例子演示了一个简单的Snakes和Ladders(也称为Chutes and Ladders)游戏:

游戏规则如下:
该板有25个方格,目的是降落在25方格或以上。
玩家的起始方格是“正方形零点”,就在板的左下角。
每回合,你掷出一个六面*子骰**,并按照上面虚线箭头指示的水平路径移动该数量的正方形。
如果你的回合在梯子的底部结束,那么你在梯子上移动。
如果你的回合结束于蛇的头部,那么你会沿着那条蛇移动。
游戏板由一组Int值表示。 它的大小基于一个叫做finalSquare的常量,用于初始化数组,并在稍后的例子中检查胜利条件。 由于玩家从棋盘上开始,在“方块零点”上,棋盘以26个零值进行初始化,而不是25个。
let finalSquare = 25
var board = [Int](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
方形3包含梯子的底部,将您移动到方块11.为了表示这个,board [03]等于+08,相当于8的整数值(3和11之间的差值)。 为了对齐值和语句,一元加运算符(+ i)明确地与一元减运算符(-i)一起使用,低于10的数字用零填充。 (风格技术不是绝对必要的,但它们导致整洁的代码。)
var square = 0
var diceRoll = 0
while square < finalSquare {
// roll the dice
diceRoll += 1
if diceRoll == 7 { diceRoll = 1 }
// move by the rolled amount
square += diceRoll
if square < board.count {
// if we’re still on the board, move up or down for a snake or a ladder
square += board[square]
}
}
print("Game over!")
上面的例子使用了非常简单的方法来掷*子骰**。不是生成一个随机数,而是以diceRoll值为0开始。每次通过while循环时,diceRoll都会加1,然后检查它是否变得过大。当这个返回值等于7时,*子骰**滚动变得太大并且被重置为值1.结果是一系列的diceRoll值,其总是1,2,3,4,5,6,1,2…..。
掷*子骰**后,玩家以掷骰方式向前移动。掷*子骰**有可能将玩家移动到方块25以外,在这种情况下游戏结束。为了应付这种情况,代码检查square小于board数组的count属性。如果方格有效,则将板[square]中存储的值添加到当前平方值,以便将玩家向上或向下移动任何梯子或蛇。
注意
如果不执行此检查,board [square]可能尝试访问板阵列边界外的值,这会触发运行时错误。
当前的while循环执行结束,并且检查循环的条件以查看是否应该再次执行循环。如果玩家已经移动到或超过25号方块,则回路的状况评估为假,游戏结束。
在这种情况下while循环是合适的,因为在while循环开始时游戏的长度并不清晰。相反,循环被执行直到满足特定的条件。
repeat-while
while循环的其他变体(称为repeat-while循环)在考虑循环条件之前首先执行一次循环块循环。然后它继续重复循环,直到条件为假。
注意
Swift中的repeat-while循环类似于其他语言的do-while循环。
以下是repeat-while循环的一般形式
-
repeat {
-
statements
-
} whilecondition
这里是“蛇和梯子”的例子,它被写成repeat-while循环而不是while循环。 finalSquare,board,square和diceRoll的值的初始化方式与while循环完全相同。
let finalSquare = 25
var board = [Int](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
var square = 0
var diceRoll = 0
在这个版本的游戏中,循环中的第一个动作是检查梯子或蛇。 棋盘上没有任何梯子将球员直接拉到25平方,所以不可能通过上梯来赢得比赛。 因此,检查一条蛇或梯子是循环中的第一个动作是安全的。
在比赛开始时,玩家处于“平方零点”。 board [0]总是等于0并且不起作用。
repeat {
// move up or down for a snake or ladder
square += board[square]
// roll the dice
diceRoll += 1
if diceRoll == 7 { diceRoll = 1 }
// move by the rolled amount
square += diceRoll
} while square < finalSquare
print("Game over!")
在代码检查蛇和梯子后,掷*子骰**,玩家通过掷*子骰**前进。当前循环执行结束。
循环的条件(while square <finalSquare)与以前相同,但是这次它不会被计算,直到循环的第一次运行结束。 repeat-while循环的结构比上一个例子中的while循环更适合这个游戏。在上面的repeat-while循环中,square + = board [square]总是在循环之后立即执行,而条件确认square仍在棋盘上。这种行为消除了在前面描述的游戏的while循环版本中看到的数组边界检查的需要。
条件声明
根据特定条件执行不同的代码通常很有用。发生错误时可能需要运行一段代码,或者当值变得过高或过低时显示消息。要做到这一点,你需要有条件的部分代码。
Swift提供了两种方法将条件分支添加到代码中:if语句和switch语句。通常情况下,您使用if语句来评估简单条件,只有几个可能的结果。 switch语句更适合于具有多种可能排列的更复杂的条件,并且在模式匹配可以帮助选择适当的代码分支来执行的情况下非常有用。
If
最简单的形式是if语句具有单个if条件。它只有在条件为真时才执行一组语句。
var temperatureInFahrenheit = 30
if temperatureInFahrenheit <= 32 {
print("It’s very cold. Consider wearing a scarf.")
}
// Prints "It’s very cold. Consider wearing a scarf."
上面的例子检查温度是否小于或等于华氏32度(水的冰点)。 如果是,则打印一条消息。 否则,不会打印任何消息,并在if语句的大括号之后继续执行代码。
如果if条件为false,if语句可以提供另一组语句,称为else子句。 这些语句由else关键字指示。
temperatureInFahrenheit = 40
if temperatureInFahrenheit <= 32 {
print("It’s very cold. Consider wearing a scarf.")
} else {
print("It’s not that cold. Wear a t-shirt.")
}
// Prints "It’s not that cold. Wear a t-shirt.”
这两个分支之一总是被执行。 由于气温已经升高到华氏40度,因此建议穿围巾并不足够冷,因此反而触发了其他分支。
您可以将多个if语句链接在一起考虑附加的子句。
temperatureInFahrenheit = 90
if temperatureInFahrenheit <= 32 {
print("It’s very cold. Consider wearing a scarf.")
} else if temperatureInFahrenheit >= 86 {
print("It’s really warm. Don’t forget to wear sunscreen.")
} else {
print("It’s not that cold. Wear a t-shirt.")
}
// Prints "It’s really warm. Don’t forget to wear sunscreen.”
在这里,增加了一个额外的if语句来响应特别温暖的温度。 最后的其他条款仍然存在,并且打印出对任何既不太热也不太冷的温度的响应。
然而,最后的else子句是可选的,如果这组条件不需要完成,可以排除它。
temperatureInFahrenheit = 72
if temperatureInFahrenheit <= 32 {
print("It’s very cold. Consider wearing a scarf.")
} else if temperatureInFahrenheit >= 86 {
print("It’s really warm. Don’t forget to wear sunscreen.")
}
switch
switch语句会考虑一个值并将其与几种可能的匹配模式进行比较。 然后根据成功匹配的第一个模式执行适当的代码块。 switch语句为if语句提供了用于响应多个潜在状态的替代方法。
以最简单的形式,switch语句将一个值与一个或多个相同类型的值进行比较。
-
switchsome value to consider {
-
casevalue 1:
-
respond to value 1
-
casevalue 2,
-
value 3:
-
respond to value 2 or 3
-
default:
-
otherwise, do something else
-
}
每个switch语句由多个可能的情况组成,每个情况都以case关键字开头。除了与特定值进行比较外,Swift还为每种情况提供了几种方式来指定更复杂的匹配模式。这些选项将在本章稍后介绍。
就像if语句的主体一样,每个case都是代码执行的独立分支。 switch语句确定应该选择哪个分支。此过程称为打开正在考虑的值。
每个switch语句都必须是详尽的。也就是说,所考虑类型的每个可能值都必须与其中一个开关情况相匹配。如果不适合为每个可能的值提供一个案例,则可以定义一个默认案例来覆盖任何未明确解决的值。此默认情况下由默认关键字指示,并且必须始终显示最后一个。
此示例使用switch语句来考虑一个名为someCharacter的单个小写字符:
let someCharacter: Character = "z"
switch someCharacter {
case"a":
print("The first letter of the alphabet")
case"z":
print("The last letter of the alphabet")
default:
print("Some other character")
}
// Prints "The last letter of the alphabet"
switch语句的第一个case匹配英文字母的第一个字母a,第二个case匹配最后一个字母z。由于交换机必须为每个可能的字符都有一个情况,而不仅仅是每个字母字符,所以此switch语句使用默认情况来匹配a和z以外的所有字符。该规定确保switch语句是详尽无遗的。
与C和Objective-C中的switch语句相比,Swift中的switch语句不会落在每个case的底部,而是默认置于下一个。相反,只要第一个匹配的开关情况完成,整个switch语句就会结束它的执行,而不需要明确的break语句。这使得switch语句比C语言更安全,更易于使用,避免错误地执行多个switch case。
注意
虽然在Swift中不需要中断,但是可以使用break语句来匹配和忽略特定的情况,或者在该情况已经完成其执行之前打破匹配的情况。有关详细信息,请参阅切换语句中的中断。
每个案例的主体必须包含至少一个可执行语句。写下面的代码是无效的,因为第一种情况是空的:
let anotherCharacter: Character = "a"
switch anotherCharacter {
case"a": // Invalid, the case has an empty body
case"A":
print("The letter A")
default:
print("Not the letter A")
}
// This will report a compile-time error.
与C中的switch语句不同,此switch语句不匹配“a”和“A”。 相反,它会报告编译时错误,即“a”情况:不包含任何可执行语句。 这种方法避免了从一种情况到另一种情况的意外损失,并且使得安全代码的意图更加清晰。
要使用与“a”和“A”匹配的单个案例进行切换,请将这两个值合并为一个复合大小写,用逗号分隔这些值。
let anotherCharacter: Character = "a"
switch anotherCharacter {
case"a", "A":
print("The letter A")
default:
print("Not the letter A")
}
// Prints "The letter A"
为了便于阅读,复合案例也可以写在多行上。 有关复合个案的更多信息,请参见复合个案。
注意
要在特定交换机案例的末尾明确贯彻,请使用Fallthrough关键字,如Fallthrough中所述。
间隔匹配
可以检查switch cases中的值是否包含在间隔中。 此示例使用数字间隔为任何大小的数字提供自然语言计数:
let approximateCount = 62
let countedThings = "moons orbiting Saturn"
let naturalCount: String
switch approximateCount {
case0:
naturalCount = "no"
case1..<5:
naturalCount = "a few"
case5..<12:
naturalCount = "several"
case12..<100:
naturalCount = "dozens of"
case100..<1000:
naturalCount = "hundreds of"
default:
naturalCount = "many"
}
print("There are \(naturalCount)\(countedThings).")
// Prints "There are dozens of moons orbiting Saturn."
在上面的例子中,approximateCount是在switch语句中求值的。 每种情况都会将该值与数字或间隔进行比较。 由于approximateCount的值介于12和100之间,因此naturalCount被分配值“几十个”,并且将执行从switch语句中移出。
元组(Tuples)
您可以使用元组在同一个switch语句中测试多个值。 元组中的每个元素都可以针对不同值或值的间隔进行测试。 或者,使用下划线字符(_)(也称为通配符模式)来匹配任何可能的值。
下面的示例采用(x,y)点,表示为类型(Int,Int)的简单元组,并将其分类在示例后面的图表中。
let somePoint = (1, 1)
switch somePoint {
case (0, 0):
print("\(somePoint) is at the origin")
case (_, 0):
print("\(somePoint) is on the x-axis")
case (0, _):
print("\(somePoint) is on the y-axis")
case (-2...2, -2...2):
print("\(somePoint) is inside the box")
default:
print("\(somePoint) is outside of the box")
}
// Prints "(1, 1) is inside the box"

The switch确定点是位于原点(0,0),红色x轴,橙色y轴,位于原点中心的蓝色4乘4框内还是框外。
与C不同,Swift允许多个开关情况考虑相同的值或值。 事实上,在这个例子中,点(0,0)可以匹配全部四种情况。 但是,如果可能有多个匹配项,则始终使用第一个匹配的案例。 点(0,0)将首先匹配大小写(0,0),因此所有其他匹配的案例都将被忽略。
价值绑定(Value Bindings)
A switch case 可以命名与临时常量或变量匹配的值,以便在案例的主体中使用。 这种行为被称为值绑定,因为值被绑定到案例正文中的临时常量或变量。
下面的例子将一个(x,y)点表示为一个类型为(Int,Int)的元组,并将它分类到下面的图表中:
let anotherPoint = (2, 0)
switch anotherPoint {
case (letx, 0):
print("on the x-axis with an x value of \(x)")
case (0, lety):
print("on the y-axis with a y value of \(y)")
caselet (x, y):
print("somewhere else at (\(x), \(y))")
}
// Prints "on the x-axis with an x value of 2"
The switch确定点是位于红色x轴上,橙色y轴上还是其他位置(在两个轴上)。
这三个switch cases声明占位符常量x和y,它们暂时从另一个点接受一个或两个元组值。第一种情况,case(let x,0)匹配任何y值为0的点,并将该点的x值赋给临时常量x。类似地,第二种情况,case(0,let y)匹配x值为0的任意点,并将该点的y值赋给临时常量y。
临时常量声明后,可以在案例的代码块中使用它们。在这里,它们用于打印点的分类。
这个switch语句没有default case。最后一种情况,let(x,y)声明了一个可以匹配任何值的两个占位符常量的元组。由于另一个点总是一个包含两个值的元组,所以这种情况下所有可能的剩余值都是匹配的,并且不需要使用default case的switch语句。
Where
A switch case可以使用where子句来检查附加条件。
下面的示例在下面的图中对(x,y)点进行分类:
let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) wherex == y:
print("(\(x), \(y)) is on the line x == y")
case let (x, y) wherex == -y:
print("(\(x), \(y)) is on the line x == -y")
case let (x, y):
print("(\(x), \(y)) is just some arbitrary point")
}
// Prints "(1, -1) is on the line x == -y"

switch语句确定点是否位于x == y的绿色对角线上,在x == -y的紫色对角线上,或者两者都不是。
这三个switch case声明了占位符常量x和y,它们暂时从yetAnotherPoint中获取两个元组值。 这些常量用作where子句的一部分,以创建动态过滤器。 只有当where子句的条件评估为true时,switch case才匹配当前的点值。
和前面的例子一样,最后一个例子匹配所有可能的剩余值,所以不需要使用默认情况来完成switch语句。
复合案例(Compound Cases)
共享相同主体的多个switch cases可以通过在 case之后写入多个模式来组合,每个模式之间以逗号分隔。 如果任何模式匹配,则认为该情况匹配。 如果列表很长,模式可以写入多行。 例如:
let someCharacter: Character = "e"
switch someCharacter {
case "a", "e", "i", "o", "u":
print("\(someCharacter) is a vowel")
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
"n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
print("\(someCharacter) is a consonant")
default:
print("\(someCharacter) is not a vowel or a consonant")
}
// Prints "e is a vowel"
switch语句的第一个例子与英语中的所有五个小写元音匹配。 同样,它的第二个案例匹配所有小写英语辅音。 最后,默认情况下匹配任何其他字符。
复合个案也可以包含值绑定。 复合大小写的所有模式都必须包含相同的一组数值绑定,并且每个绑定必须从复合大小写中的所有模式中获取相同类型的值。 这可以确保:无论案件的哪个部分匹配,案件正文中的代码都可以访问绑定的值,并且该值始终具有相同的类型。
let stillAnotherPoint = (9, 0)
switch stillAnotherPoint {
case (letdistance, 0), (0, letdistance):
print("On an axis, \(distance) from the origin")
default:
print("Not on an axis")
}
// Prints "On an axis, 9 from the origin"
上面的情况有两种模式:(let distance, 0)匹配x轴上的点,(0, letdistance)匹配y轴上的点。 两种模式都包含距离和距离的绑定,这两种模式都是一个整数 - 这意味着案例正文中的代码始终可以访问距离值。
控制传输语句
控制传输语句通过将控制从一段代码转移到另一段代码来改变您的代码的执行顺序。 Swift有五个控制转移语句:
continue 、break 、fallthrough 、return 、 throw
下面描述了 continue, break, and fallthrough 。 return语句在函数中进行了描述,并且在使用抛出函数传播错误中描述了throw语句。
continue
continue语句告诉循环停止它正在做的事情,并在循环的下一次迭*开代**始时重新开始。 它说“我已经完成了当前的循环迭代”而不会完全离开循环。
以下示例从小写字符串中删除所有元音和空格以创建一个神秘的拼图短语:
let puzzleInput = "great minds think alike"
var puzzleOutput = ""
let charactersToRemove: [Character] = ["a", "e", "i", "o", "u", " "]
for characterinpuzzleInput {
if charactersToRemove.contains(character) {
continue
} else {
puzzleOutput.append(character)
}
}
print(puzzleOutput)
// Prints "grtmndsthnklk"
上面的代码在匹配元音或空格时调用continue关键字,导致循环的当前迭代立即结束并直接跳到下一次迭代的开始。
break
break语句立即结束整个控制流语句的执行。当你想比其他情况下更早地终止switch循环语句的执行时,break语句可以在switch或loop语句中使用。
Break in a Loop Statement
在循环语句中使用时,break会立即结束循环的执行,并在控制循环的大括号(})后将控制权转交给代码。不会执行循环当前迭代的其他代码,也不会开始循环的进一步迭代。
Break in a Switch Statement
在switch语句中使用时,break会导致switch语句立即停止执行,并在switch语句的大括号(})后将控制权转交给代码。
此行为可用于匹配和忽略switch语句中的一个或多个案例。因为Swift的switch语句是详尽无遗的,并且不允许出现空例,所以有时需要刻意匹配和忽略一个案例,以便明确地表达您的意图。您可以通过将break语句写为要忽略的整个案例来做到这一点。当case语句与switch语句匹配时,case中的break语句立即结束switch语句的执行。
注意
仅包含注释的A switch case被报告为编译时错误。注释不是语句,不会导致switch case被忽略。总是使用break语句忽略切换案例。
以下示例打开Character值并确定它是否以四种语言之一表示数字符号。为简洁起见,单个switch case中涵盖了多个值。
let numberSymbol: Character = "三"// Chinese symbol for the number 3
var possibleIntegerValue: Int?
switch numberSymbol {
case"1", "١", "一", "๑":
possibleIntegerValue = 1
case"2", "٢", "二", "๒":
possibleIntegerValue = 2
case"3", "٣", "三", "๓":
possibleIntegerValue = 3
case"4", "٤", "四", "๔":
possibleIntegerValue = 4
default:
break
}
if let integerValue = possibleIntegerValue {
print("The integer value of \(numberSymbol) is \(integerValue).")
} else {
print("An integer value could not be found for \(numberSymbol).")
}
// Prints "The integer value of 三 is 3."
此示例检查numberSymbol以确定数字1到4是否是拉丁文,阿拉伯文,中文或泰文符号。如果找到匹配项,其中一个switch语句的案例将设置可选的Int?称为possibleIntegerValue的变量为适当的整数值。
switch语句完成其执行后,该示例使用可选绑定来确定是否找到值。 possibleIntegerValue变量由于是可选类型而具有隐式初始值nil,因此只有在通过switch语句的前四种情况之一将possibleIntegerValue设置为实际值时,可选绑定才会成功。
因为在上面的例子中列出每个可能的Character值是不现实的,所以默认情况下处理任何不匹配的字符。这个默认情况下不需要执行任何操作,因此它使用一个break语句作为它的主体。一旦默认情况匹配,break语句就会结束switch语句的执行,并且代码执行将继续从if语句开始。
Fallthrough
在Swift中,switch语句不会落在每个case的底部并进入下一个case。也就是说,只要第一个匹配案例完成,整个switch语句就会完成它的执行。相比之下,C要求您在每个switch case的末尾插入一个明确的break语句以防止漏掉。避免默认的延迟意味着Swift的switch语句比C中的对应语言更加简洁和可预测,因此避免了错误地执行多个开关事件。
如果您需要C风格的贯穿行为,您可以使用fallthrough关键字逐个选择加入此行为。下面的例子使用fallthrough创建一个数字的文本描述。
let integerToDescribe = 5
var description = "The number \(integerToDescribe) is"
switch integerToDescribe {
case 2, 3, 5, 7, 11, 13, 17, 19:
description += " a prime number, and also"
fallthrough
default:
description += " an integer."
}
print(description)
// Prints "The number 5 is a prime number, and also an integer."
此示例声明一个名为description的新String变量并为其分配一个初始值。该函数然后使用switch语句来考虑integerToDescribe的值。如果integerToDescribe的值是列表中的素数之一,则该函数会将文本附加到说明的结尾处,以指出该数字是素数。然后,它使用fallthrough关键字来“陷入fall into”默认情况。默认情况下会在描述的末尾添加一些额外的文本,并且switch语句已完成。
除非integerToDescribe的值在已知素数列表中,否则根本不会与第一个开关大小相匹配。由于没有其他特定情况,因此integerToDescribe与默认情况相匹配。
switch语句执行完成后,使用print(_:separator:terminator :)函数打印数字的描述。在这个例子中,数字5被正确识别为素数。
注意
fallthrough关键字不会检查导致执行陷入的开关大小写的大小写条件。 fallthrough关键字简单地使代码执行直接移动到下一个case(或default case)块内的语句,就像在C的标准switch语句行为中一样。
标记语句(Labeled Statements)
在Swift中,可以在其他循环和条件语句中嵌套循环和条件语句,以创建复杂的控制流结构。但是,循环和条件语句都可以使用break语句来提前结束它们的执行。因此,明确您想要终止break语句的循环或条件语句有时很有用。同样,如果您有多个嵌套循环,则明确说明continue语句应该影响哪个循环会很有用。
为了实现这些目标,您可以用语句标签标记循环语句或条件语句。使用条件语句,可以使用带有break语句的语句标签来结束标记语句的执行。使用循环语句,可以使用带有break或continue语句的语句标签来结束或继续执行带标签的语句。
带标签的语句通过将标签放置在与语句的介绍人关键字相同的行上,后跟冒号。下面是while循环语法的一个例子,虽然所有循环和switch语句的原理是相同的:
-
label name: whilecondition {
-
statements
-
}
以下示例使用带有标记while循环的break和continue语句来修改本章前面提到的Snakes and Ladders游戏的版本。 这一次,游戏有一个额外的规则:
要赢得比赛,你必须完全登上25号广场。
如果一个特定的掷*子骰**会使你超过25平方,你必须再次掷出,直到你掷出25号方格所需的确切数量。
游戏板和以前一样。

finalSquare,board,square和diceRoll的值的初始化方式与之前一样:
let finalSquare = 25
var board = [Int](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
var square = 0
var diceRoll = 0
该版本的游戏使用while循环和switch语句来实现游戏的逻辑。 while循环有一个名为gameLoop的语句标签来表示它是Snakes and Ladders游戏的主要游戏循环。
while循环的条件是square!= finalSquare,以反映你必须完全落在25平方上。
gameLoop: whilesquare != finalSquare {
diceRoll += 1
if diceRoll == 7 { diceRoll = 1 }
switch square + diceRoll {
case finalSquare:
// diceRoll will move us to the final square, so the game is over
break gameLoop
case let newSquarewherenewSquare > finalSquare:
// diceRoll will move us beyond the final square, so roll again
continue gameLoop
default:
// this is a valid move, so find out its effect
square += diceRoll
square += board[square]
}
}
print("Game over!")
*子骰**在每个循环开始时滚动。该循环不是立即移动*放播**器,而是使用开关语句来考虑移动的结果并确定移动是否被允许:
如果*子骰**掷出将玩家移动到最后一个方格中,则游戏结束。休息gameLoop语句将控制转移到while循环外的第一行代码,这会结束游戏。
如果掷*子骰**会将玩家移动到最后一个方格之外,则该移动无效,玩家需要再次掷骰。继续gameLoop语句结束当前while循环迭代并开始循环的下一次迭代。
在所有其他情况下,掷*子骰**是一个有效的举措。玩家通过掷骰广场向前移动,游戏逻辑检查任何蛇和梯子。循环结束,并且控制返回到while条件以决定是否需要另一转。
注意
如果上面的break语句没有使用gameLoop标签,它将跳出switch语句,而不是while语句。使用gameLoop标签可以清楚地知道应该终止哪个控制语句。
在调用继续gameLoop跳转到循环的下一次迭代时,使用gameLoop标签并不是必须的。游戏中只有一个循环,因此对于continue语句将影响哪个循环没有任何含糊之处。但是,在continue语句中使用gameLoop标签并没有什么坏处。这样做符合标签与break语句一起使用,并有助于使游戏的逻辑更加清晰,以便阅读和理解。
提前退出(Early Exit)
guard语句,如if语句,根据表达式的布尔值执行语句。 您使用guard语句来要求条件必须为真,以便执行guard语句后的代码。 与if语句不同,guard语句总是有一个else子句 - 如果条件不成立,则执行else子句中的代码。
func greet(person: [String: String]) {
guard let name = person["name"] else {
return
}
print("Hello \(name)!")
guard let location = person["location"] else {
print("I hope the weather is nice near you.")
return
}
print("I hope the weather is nice in \(location).")
}
greet(person: ["name": "John"])
// Prints "Hello John!"
// Prints "I hope the weather is nice near you."
greet(person: ["name": "Jane", "location": "Cupertino"])
// Prints "Hello Jane!"
// Prints "I hope the weather is nice in Cupertino."
如果符合guard语句的条件,则在guard语句的大括号之后继续执行代码。任何使用可选绑定作为条件的一部分赋值的变量或常量都可用于Guard语句出现的代码块的其余部分。
如果不满足该条件,则执行else分支中的代码。该分支必须传输控制以退出警戒语句出现的代码块。它可以使用控制传输语句(如return,break,continue或throw)来完成此操作,也可以调用不返回的函数或方法,例如fatalError(_:file:line :)。
与使用if语句进行同样的检查相比,使用guard要求来提高代码的可读性。它可以让你编写通常被执行的代码,而不用将其包装在一个else块中,并且它可以让代码处理违反要求的代码。
检查API可用性
Swift内置了对API可用性检查的支持,确保您不会意外使用给定部署目标上不可用的API。
编译器使用SDK中的可用性信息来验证代码中使用的所有API在项目指定的部署目标上是否可用。如果您尝试使用不可用的API,Swift会在编译时报告错误。
在if或guard语句中使用可用性条件来有条件地执行代码块,具体取决于您要使用的API是否在运行时可用。编译器在验证该代码块中的API可用时使用可用性条件中的信息。
if #available(iOS10, macOS10.12, *) {
// Use iOS 10 APIs on iOS, and use macOS 10.12 APIs on macOS
} else {
// Fall back to earlier iOS and macOS APIs
}
上面的可用性条件指定在iOS中,if语句的主体仅在iOS 10及更高版本中执行; 在macOS中,仅在macOS 10.12及更高版本中。 最后一个参数*是必需的,并指定在任何其他平台上,if的主体在您的目标指定的最小部署目标上执行。
通用形式中,可用性条件采用平台名称和版本的列表。 您可以使用iOS,macOS,watchOS和tvOS等平台名称查看完整列表,请参阅声明属性。 除了指定主要版本号(如iOS 8或MacOS 10.10)之外,您还可以指定次要版本号,如iOS 8.3和MacOS 10.10.3。
-
if#available(platform nameversion, ..., *) {
-
statements to execute if the APIs are available
-
} else {
-
fallback statements to execute if the APIs are unavailable
-
}