Swift
提供所有多样化的控制流语句。包括 while
循环来多次执行任务; if , guard 和 switch
语句来基于特定的条件执行不同的代码分支;还有比如 break
和 continue
语句来传递执行流到你代码的另一个点上。
Swift
同样添加了 for-in
循环,它让你更简便地遍历数组、字典、范围和其他序列。
Swift
的 switch
语句同样比 C
中的对应语句多了不少新功能。比如说 Swift
中的 switch
语句不再“贯穿”到下一个情况当中,这就避免了 C
中常见的 break
语句丢失问题。情况可以匹配多种模式,包括间隔匹配,元组和特定的类型。 switch
中匹配的值还能绑定到临时的常量和变量上供情况中代码使用,并且可以为每一个情况写 where
分句表达式来应用复杂条件匹配。
For-in 循环
使用 for-in
循环来遍历序列,比如一个范围的数字,数组中的元素或者字符串中的字符。
|
|
The same as Dictionary,访问它的键值对。当字典遍历时,每一个元素都返回一个 (key, value)
元组,你可以在for-in
循环体中使用显式命名常量来分解 (key, value)
元组成员,当然不想要的值使用_
来舍弃值或键。
|
|
for-in
循环同样能遍历数字区间。这个栗子打印了乘五表格的前几行:
|
|
在上面的栗子当中, index
是一个常量,它的值在每次遍历循环开始的时候被自动地设置。因此,它不需要在使用之前声明。它隐式地在循环的声明中声明了,不需要再用 let
声明关键字。
如果你不需要序列的每一个值,你可以使用 下划线 ( _
)来取代遍历名以忽略值。
使用半开区间运算符( ..< )
来包含最小值但不包含最大值
|
|
有些用户可能想要在他们的UI上少来点 分钟标记。比如说每 5 分钟一个标记吧。使用 stride(from:to:by:)
函数来跳过不想要的标记。
|
|
闭区间也同样适用,使用 stride(from:through:by:)
即可:
|
|
While 循环
while
循环执行一个合集的语句指导条件变成 false
。这种循环最好在第一次循环之后还有未知数量的遍历时使用。Swift
提供了两种 while
循环:
while
在每次循环开始的时候计算它自己的条件;repeat-while
在每次循环结束的时候计算它自己的条件。
While
while
循环通过判断单一的条件开始。如果条件为 true
,语句的合集就会重复执行直到条件变为 false
。
|
|
玩一个玩蛇与梯子(也叫滑梯与梯子)的简单栗子:
<: style=’margin: .auto’ src=’snakesAndLadders_2x.png’ alt=’snakesAndLadders_2x’ title=’滑梯与梯子’>
下边是游戏的规则:
- 棋盘拥有 25 个方格,目标就是到达或者超过第 25 号方格;
- 每一次,你扔一个六面色子,安装方格的数字移动,依据水平的线路,如图安装上边虚线箭头标注的路线;
- 如果你停留在了梯子的下边,你就可以顺着梯子爬上去;
- 如果你停留在了蛇的头上,你就要顺着蛇滑下来。
游戏棋盘用 Int
值的数组来表示。它的大小基于一个叫做 finalSquare
的常量,它被用来初始化数组同样用来检测稍后的胜利条件。棋盘使用 26 个零 Int
值初始化,而不是 25 个(从 0 到 25 ):
|
|
随后设置为拥有更多特定的值比如蛇和梯子。有梯子的方格有一个正数来让你移动到棋盘的上方,因此有蛇的方格有一个负数来让你从棋盘上倒退:
|
|
玩家从“零格”开始,它正好是棋盘的左下角。第一次扔色子总会让玩家上到棋盘上去:
|
|
当前的 while
循环执行结束,并且循环条件已经检查来看循环是否应该再次执行。如果玩家已经移到或者超出了第 25 号方格,循环评定为 false
,游戏就结束了。
while
循环在这个情况当中合适是因为开始 while
循环之后游戏的长度并不确定。循环会一直执行下去直到特定的条件不满足。
Repeat-While
while
循环的另一种形式,就是所谓的 repeat-while
循环,在判断循环条件之前会执行一次循环代码块。然后会继续重复循环直到条件为 false
。
repeat-while
循环的通用形式:
|
|
再次回顾蛇与梯子的栗子,使用 repeat-while
循环而不是 while
循环。 finalSquare , board , square , 和 diceRoll
的值初始化的方式与 while
循环完全相同:
|
|
在这个版本的游戏中,第一次循环中的动作是用来检查梯子或者蛇的。没有梯子能直接把玩家带到25格,因此不可能通过梯子赢得游戏。也就是说,在循环一开始就检查蛇还是梯子是安全的。
游戏一开始,玩家在“零格”。 board[0]
总是等于 0
的,并且没有效果:
|
|
在检查是蛇还是梯子的代码之后,就是要色子了,玩家按照 diceRoll
数量的格数前进。当前循环执行结束。
循环条件 ( while square < finalSquare)
与之前的相同,但是这次它会在第一次循环结束之后才会被判定。 repeat-while
循环的结构要比前边栗子里的 while
循环更适合这个游戏。在上边的 repeat-while
循环中, square += board[square]
总是会在循环的 while
条件确定 square
仍在棋盘上之后立即执行。这个行为就去掉了早期游戏版本中对数组边界检查的需要。
条件语句
很多时候根据特定的条件来执行不同的代码是很有用的。你可能想要在错误发生时运行额外的代码,或者当值变得太高或者太低的时候显示一条信息。要达成这个目的,你可以让你的那部分代码有条件地执行。
Swift
提供了两种方法来给你的代码添加条件分支,就是所谓的 if
语句和 switch
语句。总的来说,你可以使用 if
语句来判定简单的条件,比如少量的可能性。 switch
语句则适合更复杂的条件,比如多个可能的组合,并且在模式匹配的情况下更加有用,可以帮你选择一段合适的代码分支来执行。
If
最简单的形式中, if
语句有着一个单一的 if
条件。它只会在条件为 true
的情况下才会执行语句的集合:
|
|
先前的栗子检测了温度是否小于等于 32 华氏温度(水的冰点)。如果是,就打印一个信息。否则,没有信息打印,并且执行 if
语句的大括号后边的代码,如果温度大于32华氏温度,会执行else
分句。
Switch
switch
语句会将一个值与多个可能的模式匹配。然后基于第一个成功匹配的模式来执行合适的代码块。 switch
语句代替 if
语句提供了对多个潜在状态的响应。
在其自身最简单的格式中, switch
语句把一个值与一个或多个相同类型的值比较:
|
|
没有隐式贯穿
相比 C
和 Objective-C
里的 switch
语句来说,Swift
里的 switch
语句不会默认从每个情况的末尾贯穿到下一个情况里。相反,整个 switch
语句会在匹配到第一个 switch
情况执行完毕之后退出,不再需要显式的 break
语句。这使得 switch
语句比 C
的更安全和易用,并且避免了意外地执行多个 switch
情况。
尽管
break
在 Swift 里不是必须的,你仍然可以使用break
语句来匹配和忽略特定的情况,或者在某个情况执行完成之前就打断它。
每一个情况的函数体必须包含至少一个可执行的语句。下面的代码就是不正确的,因为第一个情况是空的:
|
|
与 C 中的 switch
语句不同,这个 switch
语句没有同时匹配 ”a” 和 ”A” 。相反它会导致一个编译时错误 case “a”:
没有包含任何可执行语句 。这可以避免意外地从一个情况贯穿到另一个情况中,并且让代码更加安全和易读。
在一个 switch
情况中匹配多个值可以用逗号分隔,并且可以写成多行,如果列表太长的话:
|
|
区间匹配
switch
情况的值可以在一个区间中匹配。这个栗子使用了数字区间来为语言中的数字区间进行转换:
|
|
在上面的栗子中, approximateCount
在 switch
语句中进行评定。每个 case
都与数字或者区间进行对比。由于 approximateCount
的值在12和100之间, naturalCount
被赋值 “dozens of”
,并且执行结果传递出了 switch
语句。
元组
你可以使用元组来在一个 switch
语句中测试多个值。每个元组中的元素都可以与不同的值或者区间进行匹配。另外,使用下划线(_)
来表明匹配所有可能的值。
|
|
switch
语句决定坐标是否在原点 (0,0)
;在红色的 x 坐标轴;在橘黄色的 y坐标轴;在蓝色的4乘4以原点为中心的方格里;或者在方格外边。
值绑定
switch
情况可以将匹配到的值*临时绑定为一个常量或者变量,来给情况的函数体使用。这就是所谓的值绑定*,因为值是在情况的函数体里“绑定”到临时的常量或者变量的。
下边的栗子接收一个 (x,y) 坐标,使用 (Int,Int) 元组类型并且在下边的图片里显示:
|
|
switch
语句决定坐标是否在在红色的x坐标轴,在橘黄色的y坐标轴;还是其他地方;或不在坐标轴上。
三个 switch
情况都使用了常量占位符 x 和 y ,它会从临时 anotherPoint
获取一个或者两个元组值。第一个情况, case(let x, 0)
,匹配任何 y 的值是 0 并且赋值坐标的x到临时常量 x 里。类似地,第二个情况, case(0,let y)
,匹配让后 x 值是 0 并且把 y 的值赋值给临时常量 y 。
在临时常量被声明后,它们就可以在情况的代码块里使用。这里,它们用来输出点的分类。
注意这个 switch 语句没有任何的 default
情况。最后的情况, case let (x,y)
,声明了一个带有两个占位符常量的元组,它可以匹配所有的值。结果,它匹配了所有剩下的值,然后就不需要 default
情况来让 switch
语句穷尽了。
Where
switch
情况可以使用 where
分句来检查额外的情况。
下边的栗子划分 (x,y)
坐标到下边的图例中:
|
|
三个 switch
情况声明了占位符常量 x 和 y ,它从 yetAnotherPoint
临时接收两个元组值。这个常量使用 where
分句,来创建动态过滤。 switch
情况只有 where
分句情况评定等于 true
时才会匹配这个值。
和前边的栗子一样,最后的情况匹配了余下所有可能的值,所以不需要 default
情况这个 switch
也是全面的。
复合情况
多个 switch
共享同一个函数体的多个情况可以在 case
后写多个模式来复合,在每个模式之间用逗号分隔。如果任何一个模式匹配了,那么这个情况都会被认为是匹配的。如果模式太长,可以把它们写成多行,比如说:
|
|
复合情况同样可以包含值绑定。所有复合情况的模式都必须包含相同的值绑定集合,并且复合情况中的每一个绑定都得有相同的类型格式。这才能确保无论复合情况的那部分匹配了,接下来的函数体中的代码都能访问到绑定的值并且值的类型也都相同。
|
|
上边的 case
拥有两个模式: (let distance, 0)
匹配 x 轴的点以及 (0, let distance)
匹配 y 轴的点。两个模式都包含一个 distance
的绑定并且 distance
在两个模式中都是整形——也就是说这个 case
函数体的代码一定可以访问 distance
的值。
控制转移语句
控制转移语句在你代码执行期间改变代码的执行顺序,通过从一段代码转移控制到另一段。Swift 拥有五种控制转移语句:
- continue
- break
- fallthrough
- return
- throw
continue
, break
, 和 fallthrough
语句在下边有详细描述。 return
语句在函数中描述,还有 throw
语句在使用抛出函数传递错误中描述。
Continue
continue
语句告诉循环停止正在做的事情并且再次从头开始循环的下一次遍历。它是说“我不再继续当前的循环遍历了”而不是离开整个的循环。
举个栗子🌰:
|
|
上面的代码在匹配到元音或者空格的时候调用了 continue
关键字,导致遍历的当前循环立即结束并直接跳到了下一次遍历的开始。这个行为使得 switch
代码块匹配(和忽略)只有元音和空格的字符,而不是请求匹配每一个要打印的字符。
Break
break
语句会立即结束整个控制流语句。当你想要提前结束 switch
或者循环语句或者其他情况时可以在 switch
语句或者循环语句中使用 break
语句。
循环语句中的 Break
当在循环语句中使用时, break
会立即结束循环的执行,并且转移控制到循环结束花括号( } )后的第一行代码上。当前遍历循环里的其他代码都不会被执行,并且余下的遍历循环也不会开始了。
Switch 语句里的 Break
当在switch
语句里使用时, break
导致 switch
语句立即结束它的执行,并且转移控制到 switch
语句结束花括号( } )之后的第一行代码上。
这可以用来在一个 switch
语句中匹配和忽略一个或者多个情况。因为 Swift 的 switch
语句是穷尽且不允许空情况的,所以有时候有必要故意匹配和忽略一个匹配到的情况以让你的意图更加明确。要这样做的话你可以通过把 break
语句作为情况的整个函数体来忽略某个情况。当这个情况通过 switch
语句匹配到了,情况中的 break
语句会立即结束 switch
语句的执行。
|
|
在上边的例子中,列举所有可能的 Character
值是不实际的,所以default
情况就提供了一个匹配所有没有匹配到的字符的功能。这个 default
情况不需要执行任何动作,所以因此就写了一个 break
语句作为函数体。一旦 default
情况匹配到了, break
语句结束 switch
语句的执行,然后代码从 if let
语句继续执行。
Fallthrough
swift
贯穿。
如果你确实需要 C
风格的贯穿行为,你可以选择在每个情况末尾使用 fallthrough
关键字。下面的栗子使用了 fallthrough
来创建一个数字的文字描述:
|
|
这个栗子声明了一个新的 String
变量叫做 description
并且赋值给它一个初始值。然后函数使用一个 switch
语句来判断 integerToDescribe
。如果 integerToDescribe
是一个列表中的质数,函数就在 description
的末尾追加文字,来标记这个数字是质数。然后它使用 fallthrough
关键字来“贯穿到” default
情况。 default
情况添加额外的文字到描述的末尾,接着 switch
语句结束。
给语句打标签
你可以内嵌循环和条件语句到其他循环和条件语句当中以在 Swift 语言中创建一个复杂的控制流结构。总之,循环和条件语句都可以使用 break
语句来提前结束它们的执行。因此,显式地标记那个循环或者条件语句是你想用 break
语句结束的就很有必要。同样的,如果你有多个内嵌循环,显式地标记你想让 continue
语句生效的是哪个循环就很有必要了。
要达到这些目的,你可以用语句标签来给循环语句或者条件语句做标记。在一个条件语句中,你可以使用一个语句标签配合 break
语句来结束被标记的语句。在循环语句中,你可以使用语句标签来配合 break
或者 continue
语句来结束或者继续执行被标记的语句。
通过把标签作为关键字放到语句开头来用标签标记一段语句,后跟冒号。这里是一个对 while
循环使用标签的栗子,这个原则对所有的循环和 switch
语句来说都相同:
|
|
下边的栗子为你之前章节看过的蛇与梯子游戏做了修改,在 while
循环中使用了标签来配合 break
和 continue
语句。这次,这个游戏有一个额外的规则:
- 要赢得游戏,你必须精确地落在第25格上。
如果特定的点数带你超过了第25格,你必须再次掷色子直到你恰好得到了落到第25格的点数。
游戏棋盘与之前的一样:finalSquare , board , square 和 diceRoll
的值也用和之前一样的方式来初始化:
|
|
这个版本的游戏使用了一个 while
循环和一个 switch
语句来实现游戏的逻辑。 while
循环有一个叫做 gameLoop
的标签,来表明它是蛇与梯子游戏的主题循环。
while
循环条件是 while square != finaleSquare
,用来反映你必须精确地落在第25格上:
|
|
每次循环,都会扔色子。使用一个 switch
语句来考虑移动的结果而不是立即移动玩家,然后如果移动允许的话就工作:
如果扔的色子将把玩家移动到最后的方格,游戏就结束。
break
gameLoop
语句转移控制到while
循环外的第一行代码上,它会结束游戏。如果扔的色子点数将会把玩家移动超过最终的方格,那么移动就是不合法的,玩家就需要再次扔色子。
continue gameLoop
语句就会结束当前的while
循环遍历并且开始下一次循环的遍历。在其他所有的情况中,色子是合法的。玩家根据
diceRoll
的方格数前进,并且游戏的逻辑会检查蛇和梯子。然后循环结束,控制返回到while
条件来决定是否要再次循环。
提前退出
guard
语句,类似于 if
语句,基于布尔值表达式来执行语句。使用 guard
语句来要求一个条件必须是真才能执行 guard
之后的语句。与 if
语句不同, guard
语句总是有一个 else
分句—— else
分句里的代码会在条件不为真的时候执行。
|
|
如果 guard
语句的条件被满足,代码会继续执行直到 guard
语句后的花括号。任何在条件中使用可选项绑定而赋值的变量或者常量在 guard
所在的代码块中随后的代码里都是**可用的**。
如果这个条件没有被满足,那么在 else
分支里的代码就会被执行。这个分支必须转移控制结束 guard
所在的代码块。要这么做可以使用控制转移语句比如 return , break , continue 或者 throw
,或者它可以调用一个不带有返回值的函数或者方法,比如 fatalError()
。
检查API的可用性
Swift 拥有内置的对 API 可用性的检查功能,它能够确保你不会悲剧地使用了对部属目标不可用的 API。
编译器在 SDK 中使用可用性信息来确保在你项目中明确的 API 都是可用的。如果你尝试使用一个不可用的 API 的话,Swift 会在编译时报告一个错误。
你可以在 if
或者 guard
语句中使用一个可用性条件来有条件地执行代码,基于在运行时你想用的哪个 API 是可用的。当验证在代码块中的 API 可用性时,编译器使用来自可用性条件里的信息来检查。
|
|
上边的可用性条件确定了在 iOS 平台, if
函数体只在 iOS 10 及以上版本才会执行;对于 macOS 平台,在只有在 macOS 10.12
及以上版本才会运行。最后一个实际参数, *
,它需求并表明在其他所有平台, if
函数体执行你在目标里明确的最小部属。
摘自:swift 官网
所有代码在Xcode9 Swift4 环境编译没问题,代码戳这里 https://github.com/keshiim/LearnSwift4