Swift提供了各种控制流程语句。这些包括while
多次执行任务的循环; if
,guard
和switch
基于特定条件执行不同代码分支的语句; 和语句,如break
和continue
对执行流在你的代码转移到另一个点。
Swift还提供了for
- in
循环,可以很容易地遍历数组,字典,范围,字符串和其它序列。
Swift的switch
声明比许多类C语言中的声明强大得多。可以匹配许多不同的模式,包括区间匹配,元组和特定类型的强制转换。switch
案例中的匹配值可以绑定到临时常量或变量以在案例正文中使用,复杂匹配条件可以用where
每个案例的子句表示。
For-In循环
使用for
- in
循环迭代序列,例如数组中的项,数字范围或字符串中的字符。
此示例使用for
- in
循环迭代数组中的项:
1 let names = ["Anna", "Alex", "Brian", "Jack"] 2 for name in names { 3 print("Hello, \(name)!") 4 } 5 // Hello, Anna! 6 // Hello, Alex! 7 // Hello, Brian! 8 // Hello, Jack!
您还可以迭代字典以访问其键值对。在字典中的每个项目返回一个元组时,该词典进行迭代,并且可以分解的元组的成员为主体内使用明确的命名常数- 回路。在下面的代码示例中,字典的键被分解为一个被调用的常量,字典的值被分解为一个被调用的常量。(key, value)
(key, value)
for
in
animalName
legCount
1 let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4] 2 for (animalName, legCount) in numberOfLegs { 3 print("\(animalName)s have \(legCount) legs") 4 } 5 // ants have 6 legs 6 // cats have 4 legs 7 // spiders have 8 legs
a的内容Dictionary
本质上是无序的,并且迭代它们并不保证它们的检索顺序。特别是,将项目插入到a Dictionary
中的顺序不会定义它们被迭代的顺序。有关数组和字典的更多信息,请参阅集合类型。
您还可以使用for
- in
具有数字范围的循环。此示例打印五次表中的前几个条目:
1 for index in 1...5 { 2 print("\(index) times 5 is \(index * 5)") 3 } 4 // 1 times 5 is 5 5 // 2 times 5 is 10 6 // 3 times 5 is 15 7 // 4 times 5 is 20 8 // 5 times 5 is 25
正如通过使用闭区域运算符[ ]所指示的那样,迭代的序列是从(包括)1
到的数字5
范围...
。值的值index
设置为range(1
)中的第一个数字,并执行循环内的语句。在这种情况下,循环只包含一个语句,该语句从五次表中打印当前值的条目index
。执行语句后,将index
更新值以包含范围(2
)中的第二个值,并print(_:separator:terminator:)
再次调用该函数。此过程将持续到达范围结束。
在上面的示例中,index
是一个常量,其值在循环的每次迭代开始时自动设置。因此,index
在使用之前不必声明。它只是通过包含在循环声明中而隐式声明,而不需要let
声明关键字。
如果不需要序列中的每个值,则可以使用下划线代替变量名来忽略这些值。
1 let base = 3 2 let power = 10 3 var answer = 1 4 for _ in 1...power { 5 answer *= base 6 } 7 print("\(base) to the power of \(power) is \(answer)") 8 // Prints "3 to the power of 10 is 59049"
上面的例子计算一个数字的值与另一个数字的幂(在这种情况下,3
计算功率10
)。它使用以开头和结尾的闭合范围将起始值1
(即,3
相对于幂0
)乘以3
十倍。对于此计算,每次循环时各个计数器值都是不必要的 - 代码只是执行循环正确的次数。用于代替循环变量的下划线字符()会导致单个值被忽略,并且在循环的每次迭代期间不提供对当前值的访问。1
10
_
在某些情况下,您可能不希望使用包含两个端点的封闭范围。考虑在表盘上绘制每分钟的刻度线。你想绘制60
刻度线,从0
分钟开始。使用半开范围运算符(..<
)包括下限但不包括上限。有关范围的更多信息,请参阅范围运算符。
1 let minutes = 60 2 for tickMark in 0..<minutes { 3 // render the tick mark each minute (60 times) 4 }
某些用户可能希望其UI中的刻度标记更少。他们可能每5
分钟更喜欢一个标记。使用此stride(from:to:by:)
功能可跳过不需要的标记。
1 let minuteInterval = 5 2 for tickMark in stride(from: 0, to: minutes, by: minuteInterval) { 3 // render the tick mark every 5 minutes (0, 5, 10, 15 ... 45, 50, 55) 4 }
也可以使用封闭范围stride(from:through:by:)
:
1 let hours = 12 2 let hourInterval = 3 3 for tickMark in stride(from: 3, through: hours, by: hourInterval) { 4 // render the tick mark every 3 hours (3, 6, 9, 12) 5 }
While循环
while
循环执行一组语句,直到条件变为false
。当在第一次迭代开始之前未知迭代次数时,最好使用这些类型的循环。Swift提供两种while
循环:
while
在每次循环开始时评估其条件。repeat
-while
在每次循环结束时评估其条件。
While
一个while
循环开始通过评估一个条件。如果条件是true
,则重复一组语句,直到条件变为false
。
这是while
循环的一般形式:
1 while condition { 2 statements 3 }
这个例子玩一个简单的蛇和梯子游戏(也称为滑道和梯子):
游戏规则如下:
- 该板有25个方格,目标是在25平方米或更高的平台上着陆。
- 玩家的起始广场是“方形零”,它位于棋盘的左下角。
- 每转一圈,你滚动一个六面骰子,按照上面虚线箭头所示的水平路径移动那个数量的方块。
- 如果你的转弯在梯子的底部结束,你就会向上移动那个梯子。
- 如果你的转弯在蛇的头部结束,你就会向下移动那条蛇。
游戏板由一系列Int
值表示。它的大小基于一个常量调用finalSquare
,用于初始化数组,并在示例后面检查获胜条件。因为玩家从“方形零”开始,所以电路板初始化为26个零Int
值,而不是25个。
1 let finalSquare = 25 2 var board = [Int](repeating: 0, count: finalSquare + 1)
然后将一些正方形设置为具有针对蛇和梯子的更具体值。带有梯子底座的正方形有一个正数可以让你向上移动,而带有蛇头的正方形有一个负数可以让你回到棋盘上。
1 board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02 2 board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
方3包含移动你到平方11.要表示这样的阶梯的底部,board[03]
是等于+08
,这相当于一个整数值8
(之间的差3
和11
)。为了对齐值和语句,一元加运算符(+i
)明确地与一元减运算符(-i
)一起使用,并且低于10
用零填充的数字。(风格技术都不是必需的,但它们会导致更简洁的代码。)
1 var square = 0 2 var diceRoll = 0 3 while square < finalSquare { 4 // roll the dice 5 diceRoll += 1 6 if diceRoll == 7 { diceRoll = 1 } 7 // move by the rolled amount 8 square += diceRoll 9 if square < board.count { 10 // if we're still on the board, move up or down for a snake or a ladder 11 square += board[square] 12 } 13 } 14 print("Game over!")
上面的例子使用一种非常简单的方法来进行骰子滚动。它不是生成随机数,而是以diceRoll
值为开头0
。每次通过while
循环,diceRoll
增加1然后检查它是否变得太大。每当此返回值等于时7
,骰子滚动变得太大并重置为值1
。结果是一个序列diceRoll
,始终是价值观1
,2
,3
,4
,5
,6
,1
,2
等。
掷骰子后,玩家向前移动diceRoll
。掷骰子可能已经将玩家移动到25以上,在这种情况下游戏结束了。为了应对这种情况,代码检查的square
数量小于board
数组的count
属性。如果square
有效,则将存储的值board[square]
添加到当前square
值,以使玩家向上或向下移动任何梯子或蛇。
注意
如果未执行此检查,board[square]
可能会尝试访问board
数组边界之外的值,这将触发运行时错误。
然后,当前while
循环执行结束,并检查循环的条件以查看是否应该再次执行循环。如果玩家已经移动或超过平方数25
,则循环的条件评估为false
并且游戏结束。
一个while
循环是在这种情况下,适当的,因为游戏的长度是不是在开始明确while
循环。相反,执行循环直到满足特定条件。
Repeat-While
while
循环的另一个变体(称为repeat
- while
循环)在考虑循环条件之前首先执行单个循环通过循环块。然后它继续重复循环,直到条件为止false
。
注意
本repeat
- while
环斯威夫特类似于一个do
- while
循环其他语言。
这里有一个的一般形式repeat
- while
循环:
1 repeat { 2 statements 3 } while condition
这里再次是Snakes and Ladders示例,写为repeat
- while
循环而不是while
循环。的值finalSquare
,board
,square
,和diceRoll
以完全相同的方式初始化为一个while
循环。
1 let finalSquare = 25 2 var board = [Int](repeating: 0, count: finalSquare + 1) 3 board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02 4 board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08 5 var square = 0 6 var diceRoll = 0
在这个版本的游戏中,循环中的第一个动作是检查梯子或蛇。棋盘上没有梯子直接将球员拉到25号方位,所以不可能通过向上移动梯子来赢得比赛。因此,检查蛇或梯子作为循环中的第一个动作是安全的。
在游戏开始时,玩家处于“方形零”。board[0]
总是等于0
并且没有效果。
1 repeat { 2 // move up or down for a snake or ladder 3 square += board[square] 4 // roll the dice 5 diceRoll += 1 6 if diceRoll == 7 { diceRoll = 1 } 7 // move by the rolled amount 8 square += diceRoll 9 } while square < finalSquare 10 print("Game over!")
在代码检查蛇和梯子之后,掷骰子并且玩家通过diceRoll
正方形向前移动。然后,当前循环执行结束。
循环的condition()与之前相同,但这次直到第一次循环结束时才会计算它。-循环的结构比上一个例子中的循环更适合这个游戏。在上面的循环,执行后立即证实循环的条件。此行为消除了在前面描述的游戏的循环版本中看到的数组边界检查的需要。while square < finalSquare
repeat
while
while
repeat
while
square += board[square]
while
square
while
条件语句
基于某些条件执行不同的代码段通常很有用。您可能希望在发生错误时运行额外的代码,或者在值变得太高或太低时显示消息。为此,您可以使代码的一部分成为条件。
Swift提供了两种方法来为代码添加条件分支:if
语句和switch
语句。通常,您使用该if
语句来评估只有少数可能结果的简单条件。该switch
语句更适合具有多种可能排列的更复杂条件,并且在模式匹配可帮助选择要执行的适当代码分支的情况下非常有用。
如果
在最简单的形式中,if
语句只有一个if
条件。它仅在条件为的情况下执行一组语句true
。
1 var temperatureInFahrenheit = 30 2 if temperatureInFahrenheit <= 32 { 3 print("It's very cold. Consider wearing a scarf.") 4 } 5 // Prints "It's very cold. Consider wearing a scarf."
上面的例子检查温度是否小于或等于32华氏度(水的冰点)。如果是,则打印一条消息。否则,不会打印任何消息,并且在if
语句的右括号后继续执行代码。
对于条件为的情况,该if
语句可以提供另一组语句,称为else子句。这些语句由关键字指示。if
false
else
1 temperatureInFahrenheit = 40 2 if temperatureInFahrenheit <= 32 { 3 print("It's very cold. Consider wearing a scarf.") 4 } else { 5 print("It's not that cold. Wear a t-shirt.") 6 } 7 // Prints "It's not that cold. Wear a t-shirt."
总是执行这两个分支中的一个。因为温度升高到40
华氏度,所以不再冷到建议戴围巾,因此else
触发分支。
您可以将多个if
语句链接在一起以考虑其他子句。
1 temperatureInFahrenheit = 90 2 if temperatureInFahrenheit <= 32 { 3 print("It's very cold. Consider wearing a scarf.") 4 } else if temperatureInFahrenheit >= 86 { 5 print("It's really warm. Don't forget to wear sunscreen.") 6 } else { 7 print("It's not that cold. Wear a t-shirt.") 8 } 9 // Prints "It's really warm. Don't forget to wear sunscreen."
在这里,增加了一个if
声明来应对特别温暖的气温。最后一个else
条款仍然存在,它会针对任何既不太温暖也不太冷的温度打印响应。
else
但是,最后一个子句是可选的,如果不需要完成条件集,则可以排除该子句。
1 temperatureInFahrenheit = 72 2 if temperatureInFahrenheit <= 32 { 3 print("It's very cold. Consider wearing a scarf.") 4 } else if temperatureInFahrenheit >= 86 { 5 print("It's really warm. Don't forget to wear sunscreen.")
由于温度既不太冷也不太热,不能触发if
或温度,因此不会打印任何信息。else if
Switch语句
一个switch
声明认为值,并确定它的几种可能匹配模式。然后,它根据成功匹配的第一个模式执行适当的代码块。一个switch
语句提供的另一种if
用于应对多种潜在状态的语句。
在最简单的形式中,switch
语句将值与一个或多个相同类型的值进行比较。
1 switch some value to consider { 2 case value 1: 3 respond to value 1 4 case value 2, 5 value 3: 6 respond to value 2 or 3 7 default: 8 otherwise, do something else 9 }
每个switch
语句都包含多个可能的案例,每个案例都以case
关键字开头。除了与特定值进行比较之外,Swift还为每种情况提供了几种方法来指定更复杂的匹配模式。这些选项将在本章后面介绍。
与if
语句的主体一样,每个语句case
都是代码执行的单独分支。该switch
语句确定应选择哪个分支。此过程称为切换正在考虑的值。
每个switch
声明都必须详尽无遗。也就是说,所考虑类型的每个可能值必须与其中一个switch
案例相匹配。如果为每个可能的值提供案例是不合适的,则可以定义默认案例以涵盖未明确解决的任何值。此默认情况由default
关键字指示,并且必须始终显示在最后。
此示例使用switch
语句来考虑名为的单个小写字符someCharacter
:
1 let someCharacter: Character = "z" 2 switch someCharacter { 3 case "a": 4 print("The first letter of the alphabet") 5 case "z": 6 print("The last letter of the alphabet") 7 default: 8 print("Some other character") 9 } 10 // Prints "The last letter of the alphabet"
该switch
陈述的第一个案例与英文字母的第一个字母匹配a
,其第二个案例与最后一个字母z匹配。因为
switch
必须具有每个可能字符的大小写,而不仅仅是每个字母字符,所以此switch
语句使用default
大小写来匹配除a
和之外的所有字符z
。该条款确保该switch
声明详尽无遗。
No implicit Fallthrough
与switch
C和Objective-C中的switch
语句相比,Swift中的语句不会在每个案例的底部落入默认情况下的下一个。相反,整个switch
语句在第一个匹配switch
案例完成后立即完成其执行,而不需要显式break
语句。这使得switch
语句比C中的语句更安全,更容易使用,并且避免switch
错误地执行多个案例。
注意
尽管break
在Swift中不需要,但您可以使用break
语句来匹配和忽略特定情况,或者在该情况完成执行之前中断匹配的情况。有关详细信息,请参阅Switch语句中的Break。
每个案例的主体必须包含至少一个可执行语句。编写以下代码无效,因为第一种情况为空:
1 let anotherCharacter: Character = "a" 2 switch anotherCharacter { 3 case "a": // Invalid, the case has an empty body 4 case "A": 5 print("The letter A") 6 default: 7 print("Not the letter A") 8 } 9 // This will report a compile-time error.
不像switch
在的C语句,该switch
语句不符合双方"a"
和"A"
。相反,它报告一个不包含任何可执行语句的编译时错误。这种方法避免了从一个案例到另一个案例的意外崩溃,并使得更安全的代码更加清晰。case "a":
为了使switch
与匹配二者的单一情况下"a"
和"A"
,这两个值组合成的化合物的情况下,用逗号分隔的值。
1 let anotherCharacter: Character = "a" 2 switch anotherCharacter { 3 case "a", "A": 4 print("The letter A") 5 default: 6 print("Not the letter A") 7 } 8 // Prints "The letter A"
为了便于阅读,复合案例也可以通过多行编写。有关复合案例的更多信息,请参阅复合案例。
注意
要在特定switch
情况的最后明确地使用,请使用fallthrough
关键字,如Fallthrough中所述。
区间匹配
switch
可以检查案例中的值是否包含在间隔中。此示例使用数字间隔为任意大小的数字提供自然语言计数:
1 let approximateCount = 62 2 let countedThings = "moons orbiting Saturn" 3 let naturalCount: String 4 switch approximateCount { 5 case 0: 6 naturalCount = "no" 7 case 1..<5: 8 naturalCount = "a few" 9 case 5..<12: 10 naturalCount = "several" 11 case 12..<100: 12 naturalCount = "dozens of" 13 case 100..<1000: 14 naturalCount = "hundreds of" 15 default: 16 naturalCount = "many" 17 } 18 print("There are \(naturalCount) \(countedThings).") 19 // Prints "There are dozens of moons orbiting Saturn."
在上面的示例中,approximateCount
将在switch
语句中进行评估。每个都case
将该值与数字或间隔进行比较。因为值approximateCount
介于12和100之间,所以naturalCount
会赋值,并且执行将从语句中转移出来。"dozens of"
switch
元组
您可以使用元组在同一switch
语句中测试多个值。可以针对不同的值或值的间隔来测试元组的每个元素。或者,使用下划线字符(_
)(也称为通配符模式)来匹配任何可能的值。
下面的示例采用(x,y)点,表示为类型的简单元组,并将其分类在示例后面的图表上。(Int, Int)
1 let somePoint = (1, 1) 2 switch somePoint { 3 case (0, 0): 4 print("\(somePoint) is at the origin") 5 case (_, 0): 6 print("\(somePoint) is on the x-axis") 7 case (0, _): 8 print("\(somePoint) is on the y-axis") 9 case (-2...2, -2...2): 10 print("\(somePoint) is inside the box") 11 default: 12 print("\(somePoint) is outside of the box") 13 } 14 // Prints "(1, 1) is inside the box"
该switch
语句确定该点是在原点(0,0),红色x轴,橙色y轴,内部原点上的蓝色4×4框内,还是框外。
与C不同,Swift允许多个switch
案例考虑相同的值或值。实际上,点(0,0)可以匹配此示例中的所有四种情况。但是,如果可以进行多次匹配,则始终使用第一个匹配的大小写。点(0,0)将首先匹配,因此将忽略所有其他匹配的情况。case (0, 0)
值绑定
一个switch
情况下,可以将其命名为临时常量或变量,在案件的身体使用相匹配的一个或多个值。此行为称为值绑定,因为值绑定到案例正文中的临时常量或变量。
下面的示例采用(x,y)点,表示为类型元组,并将其分类在下面的图表中:(Int, Int)
1 let anotherPoint = (2, 0) 2 switch anotherPoint { 3 case (let x, 0): 4 print("on the x-axis with an x value of \(x)") 5 case (0, let y): 6 print("on the y-axis with a y value of \(y)") 7 case let (x, y): 8 print("somewhere else at (\(x), \(y))") 9 } 10 // Prints "on the x-axis with an x value of 2"
该switch
语句确定该点是在红色x轴上,橙色y轴上还是其他位置(在任一轴上)。
这三种switch
情况下,声明占位符常量x
和y
,暂时采取在一个或两个元组值的anotherPoint
。第一种情况,匹配任何具有值的点,并将该点的值分配给临时常量。类似地,第二种情况匹配具有值的任何点,并将该点的值分配给临时常量。case (let x, 0)
y
0
x
x
case (0, let y)
x
0
y
y
在声明临时常量之后,可以在case的代码块中使用它们。在这里,它们用于打印点的分类。
本switch
声明没有default
案例。最后一种情况,声明了一个可以匹配任何值的两个占位符常量的元组。因为它总是两个值的元组,所以这个案例匹配所有可能的剩余值,并且不需要一个案例来使语句详尽无遗。case let (x, y)
anotherPoint
default
switch
Where
一个switch
情况下可以使用where
子句来检查附加条件。
以下示例对下图中的(x,y)点进行了分类:
1 let yetAnotherPoint = (1, -1) 2 switch yetAnotherPoint { 3 case let (x, y) where x == y: 4 print("(\(x), \(y)) is on the line x == y") 5 case let (x, y) where x == -y: 6 print("(\(x), \(y)) is on the line x == -y") 7 case let (x, y): 8 print("(\(x), \(y)) is just some arbitrary point") 9 } 10 // Prints "(1, -1) is on the line x == -y"
该switch
陈述确定该点是否在绿色对角线上,在紫色对角线上,或者两者都没有。x == y
x == -y
这三种switch
情况下,声明占位符常量x
和y
,暂时采取从两元组值yetAnotherPoint
。这些常量用作where
子句的一部分,以创建动态过滤器。仅当子句的条件求switch
值为该值时,该案例才匹配当前值。point
where
true
与前面的示例一样,最终的大小写匹配所有可能的剩余值,因此default
不需要一个案例来使switch
语句详尽无遗。
复合case
共享相同主体的多个开关盒可以通过在其后写入多个模式来组合,case
每个模式之间使用逗号。如果任何模式匹配,则认为该情况匹配。如果列表很长,则可以在多行上写入模式。例如:
1 let someCharacter: Character = "e" 2 switch someCharacter { 3 case "a", "e", "i", "o", "u": 4 print("\(someCharacter) is a vowel") 5 case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", 6 "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z": 7 print("\(someCharacter) is a consonant") 8 default: 9 print("\(someCharacter) is not a vowel or a consonant") 10 } 11 // Prints "e is a vowel"
该switch
声明的第一个案例与英语中的所有五个小写元音相匹配。同样,它的第二个案例匹配所有小写英语辅音。最后,default
案例与任何其他角色匹配。
复合案例还可以包括值绑定。复合案例的所有模式都必须包含同一组值绑定,并且每个绑定必须从复合案例中的所有模式中获取相同类型的值。这确保了,无论复合案例的哪个部分匹配,案例正文中的代码总是可以访问绑定的值,并且值始终具有相同的类型。
1 let stillAnotherPoint = (9, 0) 2 switch stillAnotherPoint { 3 case (let distance, 0), (0, let distance): 4 print("On an axis, \(distance) from the origin") 5 default: 6 print("Not on an axis") 7 } 8 // Prints "On an axis, 9 from the origin"
在case
上面有两个模式:在x轴的匹配点和在y轴一致点。两种模式都包含绑定,并且在两种模式中都是整数 - 这意味着可以始终访问其中的代码。(let distance, 0)
(0, let distance)
distance
distance
case
distance
控制转移声明
控制转移语句通过将控制从一段代码转移到另一段代码来改变代码执行的顺序。Swift有五个控制转移语句:
1 continue 2 break 3 fallthrough 4 return 5 throw
的continue
,break
和fallthrough
语句描述如下。函数中return
描述了该语句,并且在使用投掷函数传播错误中描述了该语句。throw
continue
continue
语句告诉循环停止它正在做什么,并在循环的下一次迭代开始时再次启动。它说“我完成了当前的循环迭代”而没有完全离开循环。
以下示例从小写字符串中删除所有元音和空格,以创建一个神秘的拼图短语:
1 let puzzleInput = "great minds think alike" 2 var puzzleOutput = "" 3 let charactersToRemove: [Character] = ["a", "e", "i", "o", "u", " "] 4 for character in puzzleInput { 5 if charactersToRemove.contains(character) { 6 continue 7 } 8 puzzleOutput.append(character) 9 } 10 print(puzzleOutput) 11 // Prints "grtmndsthnklk"
上面的代码continue
在匹配元音或空格时调用关键字,导致循环的当前迭代立即结束并直接跳转到下一次迭代的开始。
break
该break
语句立即结束整个控制流语句的执行。当你想要比其他情况更早地终止or语句的执行时,break
可以在一个switch
或循环语句中使用该语句switch
。
break循环声明
当在循环语句中使用时,break
立即结束循环的执行并在循环的右括号(}
)之后将控制转移到代码。不执行来自当前循环迭代的进一步代码,并且不再开始循环的迭代。
中断Switch语句
在switch
语句中使用时,break
会使switch
语句立即结束其执行,并在switch
语句的右括号(}
)后将控制权转移给代码。
此行为可用于匹配和忽略switch
语句中的一个或多个案例。由于斯威夫特的switch
陈述是详尽的,并且不允许空案件,因此有时需要故意匹配并忽略案例,以使您的意图明确。您可以通过将break
语句写为要忽略的案例的整个主体来完成此操作。当该情况与switch
语句匹配时,案例break
内的switch
语句立即结束语句的执行。
注意
一个switch
仅包含注释的情况下被报告为编译时错误。注释不是陈述,不会导致switch
案例被忽略。始终使用break
语句来忽略switch
案例。
以下示例打开一个Character
值,并确定它是否表示四种语言之一的数字符号。为简洁起见,单个switch
案例涵盖了多个值。
1 let numberSymbol: Character = "三" // Chinese symbol for the number 3 2 var possibleIntegerValue: Int? 3 switch numberSymbol { 4 case "1", "١", "一", "๑": 5 possibleIntegerValue = 1 6 case "2", "٢", "二", "๒": 7 possibleIntegerValue = 2 8 case "3", "٣", "三", "๓": 9 possibleIntegerValue = 3 10 case "4", "٤", "四", "๔": 11 possibleIntegerValue = 4 12 default: 13 break 14 } 15 if let integerValue = possibleIntegerValue { 16 print("The integer value of \(numberSymbol) is \(integerValue).") 17 } else { 18 print("An integer value could not be found for \(numberSymbol).") 19 } 20 // Prints "The integer value of 三 is 3."
本示例检查numberSymbol
以确定它是否是数字拉丁,阿拉伯语,中国,泰国或符号1
来4
。如果找到匹配项,则其中一个switch
语句的案例会将一个可选Int?
变量possibleIntegerValue
设置为适当的整数值。
在之后switch
的语句执行完成后,该示例使用可选的结合来确定值是否被发现。由于是一个可选类型,该possibleIntegerValue
变量具有隐式初始值nil
,因此只有possibleIntegerValue
在switch
语句的前四种情况之一设置为实际值时,可选绑定才会成功。
因为Character
在上面的示例中列出每个可能的值是不切实际的,所以一个default
案例处理任何不匹配的字符。这种default
情况不需要执行任何操作,因此它使用单个break
语句作为其正文。只要default
案例匹配,break
语句就会结束switch
语句的执行,并从语句继续执行代码。if let
fallthrough
在Swift中,switch
语句不会落入每个案例的底部并进入下一个案例。也就是说,switch
一旦第一个匹配的案例完成,整个语句就完成了它的执行。相比之下,C要求您break
在每个switch
案例的末尾插入一个明确的语句,以防止通过。避免默认的下降意味着Swift switch
语句比C中的对应语句更简洁和可预测,因此它们避免switch
错误地执行多个案例。
如果您需要C风格的直通行为,则可以根据具体情况选择使用fallthrough
关键字来选择此行为。以下示例fallthrough
用于创建数字的文本描述。
1 let integerToDescribe = 5 2 var description = "The number \(integerToDescribe) is" 3 switch integerToDescribe { 4 case 2, 3, 5, 7, 11, 13, 17, 19: 5 description += " a prime number, and also" 6 fallthrough 7 default: 8 description += " an integer." 9 } 10 print(description) 11 // Prints "The number 5 is a prime number, and also an integer."
此示例声明一个String
名为的新变量,description
并为其指定一个初始值。然后,该函数考虑integerToDescribe
使用switch
语句的值。如果值integerToDescribe
是列表中的素数之一,则函数将文本附加到末尾description
,以注意该数字是素数。然后它使用fallthrough
关键字“落入” default
案例。该default
案例在描述的末尾添加了一些额外的文本,并且switch
语句已完成。
除非值integerToDescribe
在已知素数列表中,否则它与第一种switch
情况完全不匹配。因为没有其他具体案例,integerToDescribe
所以与default
案例相匹配。
在之后switch
的语句执行完毕,数量的描述是使用打印print(_:separator:terminator:)
功能。在此示例中,数字5
被正确识别为素数。
注意
该fallthrough
关键字不检查的情况下条件,switch
它使执行陷入如此。的fallthrough
关键字简单地使代码执行直接移动到下一个的情况下(或内的语句default
的情况下)嵌段,在C的标准switch
语句的行为。
Label语句
在Swift中,您可以在其他循环和条件语句中嵌套循环和条件语句,以创建复杂的控制流结构。但是,循环和条件语句都可以使用该break
语句过早地结束其执行。因此,有必要明确说明您希望break
语句终止的循环或条件语句。类似地,如果您有多个嵌套循环,那么明确该continue
语句应该影响哪个循环可能很有用。
要实现这些目标,可以使用语句标签标记循环语句或条件语句。使用条件语句,可以使用带语句的语句标签break
来结束带标签语句的执行。使用循环语句,您可以使用带有break
或continue
语句的语句标签来结束或继续执行带标签的语句。
标记语句通过在与语句的introductioncer关键字相同的行上放置标签来指示,后跟冒号。这是while
循环的这种语法的一个例子,尽管所有循环和switch
语句的原理是相同的:
label name: while condition { statements }
下面的示例使用带有标记循环的break
和continue
语句,以获得本章前面while
所述的Snakes and Ladders游戏的改编版本。这一次,游戏有一个额外的规则:
- 要赢,你必须准确降落在25号广场上。
如果一个特定的骰子卷超过25平方,你必须再次滚动,直到你滚动所需的确切数字落在方形25上。
游戏板和以前一样。
的值finalSquare
,board
,square
,并diceRoll
以同样的方式为前初始化:
1 let finalSquare = 25 2 var board = [Int](repeating: 0, count: finalSquare + 1) 3 board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02 4 board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08 5 var square = 0 6 var diceRoll = 0
这个版本的游戏使用while
循环和switch
语句来实现游戏的逻辑。该while
环有一个说法叫做标签gameLoop
,以表明它是主要的游戏循环的蛇和梯子游戏。
该while
循环的条件为,以反映您必须准确降落在广场25。while square != finalSquare
1 gameLoop: while square != finalSquare { 2 diceRoll += 1 3 if diceRoll == 7 { diceRoll = 1 } 4 switch square + diceRoll { 5 case finalSquare: 6 // diceRoll will move us to the final square, so the game is over 7 break gameLoop 8 case let newSquare where newSquare > finalSquare: 9 // diceRoll will move us beyond the final square, so roll again 10 continue gameLoop 11 default: 12 // this is a valid move, so find out its effect 13 square += diceRoll 14 square += board[square] 15 } 16 } 17 print("Game over!")
骰子在每个循环开始时滚动。循环使用switch
语句来考虑移动的结果并确定是否允许移动,而不是立即移动播放器:
- 如果骰子掷骰将玩家移动到最后的方格,则游戏结束。该语句将控制转移到循环外的第一行代码,结束游戏。
break gameLoop
while
- 如果骰子掷出将移动玩家超越最后的广场,此举是无效的,玩家需要再次滚动。该语句结束当前循环迭代并开始循环的下一次迭代。
continue gameLoop
while
- 在所有其他情况下,掷骰子是有效的举动。玩家通过
diceRoll
正方形向前移动,游戏逻辑检查任何蛇和梯子。然后循环结束,控制返回到while
条件以决定是否需要另一个转弯。
注意
如果break
上面的陈述没有使用gameLoop
标签,它将突破switch
声明,而不是while
声明。使用gameLoop
标签可以清楚地终止哪个控制语句。
gameLoop
在调用跳转到循环的下一次迭代时,不一定要使用标签。游戏中只有一个循环,因此对于语句将影响哪个循环没有歧义。但是,在声明中使用标签没有任何害处。这样做符合标签与声明的一起使用,有助于使游戏的逻辑更清晰,易读和理解。continue gameLoop
continue
gameLoop
continue
break
guard语句
甲guard
语句,像一个if
语句,执行根据表达式的布尔值的语句。您使用guard
语句要求条件必须为true才能guard
执行语句后的代码。与if
语句不同,guard
语句总是有一个else
子句 - else
如果条件不为真,则执行子句中的代码。
1 func greet(person: [String: String]) { 2 guard let name = person["name"] else { 3 return 4 } 5 6 print("Hello \(name)!") 7 8 guard let location = person["location"] else { 9 print("I hope the weather is nice near you.") 10 return 11 } 12 13 print("I hope the weather is nice in \(location).") 14 } 15 16 greet(person: ["name": "John"]) 17 // Prints "Hello John!" 18 // Prints "I hope the weather is nice near you." 19 greet(person: ["name": "Jane", "location": "Cupertino"]) 20 // Prints "Hello Jane!" 21 // Prints "I hope the weather is nice in Cupertino."
如果满足guard
语句的条件,则在guard
语句的右括号后继续执行代码。使用可选绑定作为条件的一部分分配值的任何变量或常量都可用于guard
语句出现的其余代码块。
如果不满足该条件,else
则执行分支内的代码。该分支必须转移控制以退出guard
语句出现的代码块。它可以做到这一点与控制权转移的语句,如return
,break
,continue
,或者throw
,也可以调用一个函数或方法不返回,如fatalError(_:file:line:)
。
guard
与对if
语句进行相同的检查相比,使用需求语句可以提高代码的可读性。它允许您编写通常执行的代码而无需将其包装在else
块中,并且它允许您保留处理需求旁边违反要求的代码。
检查API可用性
Swift内置支持检查API可用性,这可确保您不会意外使用在给定部署目标上不可用的API。
编译器使用SDK中的可用性信息来验证代码中使用的所有API是否在项目指定的部署目标上可用。如果您尝试使用不可用的API,Swift会在编译时报告错误。
您可以在or 语句中使用可用性条件来有条件地执行代码块,具体取决于您要使用的API是否在运行时可用。当编译器验证该代码块中的API可用时,编译器将使用可用性条件中的信息。if
guard
1 if #available(iOS 10, macOS 10.12, *) { 2 // Use iOS 10 APIs on iOS, and use macOS 10.12 APIs on macOS 3 } else { 4 // Fall back to earlier iOS and macOS APIs 5 }
上面的可用性条件指定在iOS中,if
语句的主体仅在iOS 10及更高版本中执行; 在macOS中,仅在macOS 10.12及更高版本中。最后一个参数*
是必需的,并指定在任何其他平台上,在if
目标指定的最小部署目标上执行的主体。
在一般形式中,可用性条件采用平台名称和版本的列表。您可以使用平台的名称,如iOS
,macOS
,watchOS
,和tvOS
-对于完整列表,请参阅声明属性。除了指定主要版本号(如iOS 8或macOS 10.10)之外,您还可以指定次要版本号,如iOS 11.2.6和macOS 10.13.3。
1 if #available(platform name version, ..., *) { 2 statements to execute if the APIs are available 3 } else { 4 fallback statements to execute if the APIs are unavailable 5 }