您的位置:首页 > 移动开发 > Swift

The Swift Programming Language学习笔记(六)——控制流

2016-02-21 15:14 387 查看
控制流
for循环
for-in

for

while循环
while

repeat-while

条件语句
if

switch
不存在隐式的贯穿

区间匹配

元组

值绑定

where

控制转移语句
continue

break
循环语句中的break

switch语句中的break

fallthrough

带标签的语句

提前退出

检测API可用性

控制流

Swift提供了类似C语言的流程控制结构,包括可以多次执行任务的
for
while
循环,基于特定条件选择执行不同代码分支的
if
guard
switch
语句,还有控制流程跳转到其他代码的
break
continue
语句。

除了C语言里面传统的for循环,Swift还增加了
for-in
循环,用来更简单地遍历数组(array),字典(dictionary),区间(range),字符串(string)和其他序列类型。

Swift的
switch
语句比C语言中更加强大。在C语言中,如果某个case不小心漏写了
break
,这个case就会贯穿至下一个case,Swift无需写
break
,所以不会发生这种贯穿的情况。case还可以匹配更多的类型模式,包括区间匹配(range matching),元组(tuple)和特定类型的描述。
switch
的case语句中匹配的值可以是由case体内部临时的常量或者变量决定,也可以由
where
分句描述更复杂的匹配条件。

for循环

Swift提供了两种
for
循环形式

for-in
循环对一个集合里面的每个元素执行一系列语句。

for循环,用来重复执行一系列语句直到达成特定条件达成,一般通过在每次循环完成后增加计数器的值来实现。

for-in

使用
for-in
循环来遍历一个集合里面的所有元素,例如由数字表示的区间、数组中的元素、字符串中的字符。

for i in 1...5 {
print("\(i)", terminator: "-")
}


上面的
i
是一个每次循环遍历开始时被自动赋值的常量。这种情况下,i在使用前不需要声明,只需要将它包含在循环的声明中,就可以对其进行隐式声明,而无需使用let关键字声明

如果你不需要知道区间序列内每一项的值,你可以使用下划线(
_
)替代变量名来忽略对值的访问。

for _ in 0..<5 {
print("Hello")
}


使用
for-in
遍历数组和字典。遍历字典时,字典的每项元素会以
(key, value)
元组的形式返回,你可以在
for-in
循环中使用显式的常量名称来解读
(key, value)
元组。

let a = [1, 2, 3]
for i in a {    // 注意:文档上说i是常量,但是貌似只能写var不能写let
print(i)
}

let b = ["A": 1, "B": 2, "C": 3]
for var (k, v) in b {       // 注意:文档上说k和v是常量,但是貌似只能写var不能写let
print("\(k) => \(v)")
}


for

除了
for-in
循环,Swift提供使用条件判断和递增方法的标准C样式
for
循环。

for var i = 0; i < 3; ++i {     // 注意:此时的var必须写,而且不能写成let
print(i)
}


在初始化表达式中声明的常量和变量(比如
var i = 0
)只在
for
循环的生命周期里有效。如果想在循环结束后访问
i
的值,你必须要在循环生命周期开始前声明
i


while循环

这类循环适合使用在第一次迭代前迭代次数未知的情况下。Swift提供两种
while
循环形式:

while
循环,每次在循环开始时计算条件是否符合

repeat-while
循环,每次在循环结束时计算条件是否符合

while

while
循环从计算单一条件开始。如果条件为
true
,会重复运行一系列语句,直到条件变为
false


/**
* 文档上的蛇和梯子的小游戏,也叫做滑道和梯子
*/

let finalSquare = 25
var board = [Int](count: finalSquare + 1, repeatedValue: 0)

board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -
4000
11; board[22] = -02; board[24] = -08

var square = 0
var diceRoll = 0
while square < finalSquare {
// 掷骰子
if ++diceRoll == 7 { diceRoll = 1 }
// 根据点数移动
square += diceRoll
if square < board.count {
// 如果玩家还在棋盘上,顺着梯子爬上去或者顺着蛇滑下去
square += board[square]
}
}
print("Game over!")
print("最终位置是\(square)。")     // 最终位置是27。


repeat-while

它和
while
的区别是在判断循环条件之前,先执行一次循环的代码块,然后重复循环直到条件为
false
。Swift语言的
repeat-while
循环和其他语言中的
do-while
循环是类似的。

/**
* 文档上的蛇和梯子的小游戏,也叫做滑道和梯子
*/

let finalSquare = 25
var board = [Int](count: finalSquare + 1, repeatedValue: 0)

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
repeat {
// 顺着梯子爬上去或者顺着蛇滑下去
square += board[square]
// 掷骰子
if ++diceRoll == 7 { diceRoll = 1 }
// 根据点数移动
square += diceRoll
} while square < finalSquare
print("Game over!")
print("最终位置是\(square)。")     // 最终位置是27。


可以看出,此时
repeat-while
表现得比
while
循环更好。
repeat-while
方式会在条件判断
square
没有超出后直接运行
square += board[square]
,这种方式可以去掉
while
版本中的数组越界判断



条件语句

Swift提供两种类型的条件语句:
if
语句和
switch
语句。通常,当条件较为简单且可能的情况很少时,使用
if
语句。而
switch
语句更适用于条件较复杂、可能情况较多且需要用到模式匹配(pattern-matching)的情境。

if

if
语句最简单的形式就是只包含一个条件,当且仅当该条件为
true
时,才执行相关代码。

if
语句允许二选一,也就是当条件为
false
时,执行
else
语句。

还可以把多个
if
语句链接在一起,此时,最后的
else
语句是可选的

let a = 10
if a < 10 {
print("a < 10")
} else if a > 10 {
print("a > 10")
} else {
print("a == 10")
}


switch

switch
语句会尝试把某个值与若干个模式(pattern)进行匹配。根据第一个匹配成功的模式,
switch
语句会执行对应的代码。当有可能的情况较多时,通常用
switch
语句替换
if
语句。

switch
语句必须是完备的
。这就是说,每一个可能的值都必须至少有一个case分支与之对应。在某些不可能涵盖所有值的情况下,你可以使用默认(
default
)分支满足该要求,这个默认分支必须在
switch
语句的最后面。
switch
语句不完备时,默认(
default
)分支必须有


不存在隐式的贯穿

与C语言和Objective-C中的
switch
语句不同,在Swift中,当匹配的case分支中的代码执行完毕后,程序会终止
switch
语句,而不会继续执行下一个case分支。这也就是说,不需要在case分支中显式地使用
break
语句
。这使得
switch
语句更安全、更易用,也避免了因忘记写
break
语句而产生的错误。

虽然在Swift中
break
不是必须的,但你依然可以在case分支中的代码执行完毕前使用
break
跳出


每一个 case 分支都必须包含至少一条语句,避免了意外地从一个case分支贯穿到另外一个,使得代码更安全、也更直观。

一个case也可以包含多个模式,用逗号把它们分开(如果太长了也可以分行写)。

let a = 1
switch a {
// case 0:     // 每个case分支不能有空语句
case 1:
print("1")
case 2, 3:
print("2 or 3")
default:    // 此时switch还不完备,因此必须有default分支
print("Error")
}


区间匹配

case 分支的模式也可以是一个值的区间。

闭区间操作符(
...
)以及半开区间操作符(
..<
)功能被重载去返回
IntervalType
Range
。一个区间可以决定他是否包含特定的元素,就像当匹配一个
switch
声明的
case
一样。区间是一个连续值的集合,可以用
for-in
语句遍历它。

let a = 1
switch a {
case 0...1:
print("0 ~ 1")
case 2..<10:
print("2 ~ 9")
default:    // 必须有default分支
print("other")
}


元组

可以使用元组在同一个
switch
语句中测试多个值。元组中的元素可以是值,也可以是区间。另外,使用下划线(
_
)来匹配所有可能的值


let a = (1, 1)
switch a {
case (0, 0):
print("在原点")
case (_, 0):
print("在x轴")
case (0, _):
print("在y轴")
case (-1...1, -1...1):
print("在格子里")   // 在格子里
default:
print("在格子外")
}

let b = (0, 0)
switch b {
case (0, 0):
print("在原点")    // 在原点,优先匹配上!
case (_, 0):
print("在x轴")
case (0, _):
print("在y轴")
case (-1...1, -1...1):
print("在格子里")
default:
print("在格子外")
}


不像C语言,Swift允许多个case匹配同一个值。实际上,在这个例子中,点(0, 0)可以匹配所有四个case。但是,如果存在多个匹配,那么只会执行第一个被匹配到的case分支。考虑点(0, 0)会首先匹配case(0, 0),因此剩下的能够匹配(0, 0)的case分支都会被忽视掉。

值绑定

case分支的模式允许将匹配的值绑定到一个临时的常量或变量,这些常量或变量在该case分支里就可以被引用了——这种行为被称为值绑定(value binding)。

let a = (0, 1)
switch a {
case (let x, 0):
print("1、\(x)")
case (0, let x):
print("2、\(x)")     // 2、1
case let (x, y):
print("3、\(x),\(y)")    // 不需要default,因为已经完备
}

let b = (3, 0)
switch b {
case (let x, 0):        // 1、3
print("1、\(x)")
case (0, let x):
print("2、\(x)")
case let (x, y):
print("3、\(x),\(y)")
}

let c = (23, -12)
switch c {
case (let x, 0):
print("1、\(x)")
case (0, let x):
print("2、\(x)")
case let (x, y):
print("3、\(x),\(y)")    // 3、23,-12
}

let d = (0, 0)
switch d {
case (let x, 0):
print("1、\(x)")        // 1、0
case (0, let x):
print("2、\(x)")
case let (x, y):
print("3、\(x),\(y)")
}


这三个case都声明了常量
x
y
的占位符,用于
decf
临时获取元组中的一个或两个值。一旦声明了这些临时的常量,它们就可以在其对应的
case
分支里引用。

第一个
case
将匹配一个纵坐标为0的点,并把这个点的横坐标赋给临时的常量x。类似的,第二个
case
将匹配一个横坐标为0的点,并把这个点的纵坐标赋给临时的常量y


这个
switch
语句不包含默认分支。这是因为最后一个
case
声明了一个可以匹配余下所有值的元组。这使得
switch
语句已经完备了
!

这里x和y是常量,这是因为没有必要在其对应的
case
分支中修改它们的值。然而,它们也可以是变量,那么程序将会创建临时变量,并用相应的值初始化它。修改这些变量只会影响其对应的
case
分支。

where

case
分支的模式可以使用
where
语句来判断额外的条件。

let a = (1, -1)
switch a {
case let (x, y) where x == y:
print("x == y")
case let (x, y) where x == -y:
print("x == -y")    // x == -y
case let (x, y):        // 已经完备,无需default
print(a)
}


上面声明的常量
x
y
被用作
where
语句的一部分,从而创建一个动态的过滤器(filter)。当且仅当
where
语句的条件为
true
时,匹配到的
case
分支才会被执行。

控制转移语句

控制转移语句改变你代码的执行顺序,通过它你可以实现代码的跳转。Swift有五种控制转移语句

continue


break


fallthrough


return
(“函数”一节中介绍)

throw
(“错误抛出”一节中介绍)

continue

continue
语句告诉一个循环体立刻停止本次循环迭代,重新开始下次循环迭代。

在一个带有条件和递增的
for
循环体中,调用
continue
语句后,迭代增量仍然会被计算求值。循环体继续像往常一样工作,仅仅只是循环体中的执行代码会被跳过。

break

break
语句会立刻结束整个控制流的执行。当你想要更早的结束一个
switch
代码块或者一个循环体时,你都可以使用
break
语句。

循环语句中的break

当在一个循环体中使用
break
时,会立刻中断该循环体的执行,然后跳转到表示循环体结束的大括号(
}
)后的第一行代码。不会再有本次循环迭代的代码被执行,也不会再有下次的循环迭代产生。

switch语句中的break

当在一个
switch
代码块中使用
break
时,会立即中断该
switch
代码块的执行,并且跳转到表示
switch
代码块结束的大括号(
}
)后的第一行代码。

这种特性可以被用来匹配或者忽略一个或多个分支。因为Swift的
switch
需要包含所有的分支而且不允许有为空的分支,有时为了使你的意图更明显,需要特意匹配或者忽略某个分支。那么当你想忽略某个分支时,可以在该分支内写上
break
语句。当那个分支被匹配到时,分支内的
break
语句立即结束
switch
代码块。

注意:当一个
switch
分支仅仅包含注释时,会被报编译时错误。注释不是代码语句而且也不能让
switch
分支达到被忽略的效果。你总是可以使用
break
来忽略某个分支。

fallthrough

如果你确实需要C风格的贯穿的特性,你可以在每个需要该特性的
case分支中使用
fallthrough`关键字。

fallthrough
关键字不会检查它下一个将会落入执行的
case
中的匹配条件。
fallthrough
简单地使代码执行继续连接到下一个
case
中的执行代码,这和C语言标准中的
switch
语句特性是一样的。

let a = 3
switch a {
case 0, 1, 2:
print("0 ~ 2")
case 3..<100:
print("3 ~ 100")    // 3 ~ 100
fallthrough
default:
print("Over")       // Over
}


带标签的语句

在Swift中,你可以在循环体和
switch
代码块中嵌套循环体和
switch
代码块来创造复杂的控制流结构。然而,循环体和
switch
代码块两者都可以使用
break
语句来提前结束整个方法体。因此,显式地指明
break
语句想要终止的是哪个循环体或者
switch
代码块,会很有用。类似地,如果你有许多嵌套的循环体,显式指明
continue
语句想要影响哪一个循环体也会非常有用。

可以使用标签来标记一个循环体或者
switch
代码块,当使用
break
或者
continue
时,带上这个标签,可以控制该标签代表对象的中断或者执行。产生一个带标签的语句是通过在该语句的关键词的同一行前面放置一个标签,并且该标签后面还需带着一个冒号。

/**
* 文档上的蛇和梯子的小游戏,也叫做滑道和梯子。
* 现在增加一个条件:为了获胜,你必须刚好落在第 25 个方块中。即如果某次掷骰子使你的移动超出第 25 个方块,你必须重新掷骰子,直到你掷出的骰子数刚好使你能落在第 25 个方块中。
*/
let finalSquare = 25
var board = [Int](count: finalSquare + 1, repeatedValue: 0)

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

gameLoop: while square != finalSquare {
if ++diceRoll == 7 { diceRoll = 1 }
switch square + diceRoll {
case finalSquare:
// 到达最后一个方块,游戏结束
print(square + diceRoll)
break gameLoop
case let newSquare where newSquare > finalSquare:
// 超出最后一个方块,再掷一次骰子
print("square = \(square), diceRoll = \(diceRoll)")
continue gameLoop
default:
// 本次移动有效
square += diceRoll
square += board[square]
}
}
print("Game over!")
/*
过程是:23+1 => 24-8 => 16+2 => 18+3 => 21+4 => 25
square = 23, diceRoll = 4
square = 23, diceRoll = 5
square = 23, diceRoll = 6
25
Game over!
*/


提前退出

if
语句一样,
guard
的执行取决于一个表达式的布尔值。我们可以使用
guard
语句来要求条件必须为真时,以执行
guard
语句后的代码。不同于
if
语句,一个
guard
语句总是有一个
else
分句,如果条件不为真则执行
else
分句中的代码。

如果
guard
语句的条件被满足,则在保护语句的封闭大括号结束后继续执行代码。任何使用了可选绑定作为条件的一部分并被分配了值的变量或常量对于剩下的保护语句出现的代码段是可用的。

如果条件不被满足,在
else
分支上的代码就会被执行。这个分支必须转移控制以退出
guard
语句出现的代码段
它可以用控制转移语句如return,break,continue或者throw做这件事,或者调用一个不返回的方法或函数,例如fatalError()

相比于可以实现同样功能的
if
语句,按需使用
guard
语句会提升我们代码的可靠性。 它可以使你的代码连贯的被执行而不需要将它包在else块中,它可以使你处理违反要求的代码使其接近要求


let a = 10
/*
guard a < 5 else {      // error: 'guard' body may not fall through, consider using 'return' or 'break' to exit the scope
print("a >= 5")
}
*/

while true {
guard a < 5 else {
print("a >= 5")     // a >= 5
break   // 必须写break,否则报上面的错误
}
}

print("...")


检测API可用性

Swift有检查API可用性的内置支持,这可以确保我们不会不小心地使用对于当前部署目标不可用的API。

编译器使用SDK中的可用信息来验证我们的代码中使用的所有API在项目指定的部署目标上是否可用。如果我们尝试使用一个不可用的API,Swift会在编译期报错。

我们使用一个可用性条件在一个
if
guard
语句中去有条件地执行一段代码,这取决于我们想要使用的API是否在运行时是可用的。编译器使用从可用性条件语句中获取的信息去验证在代码块中调用的API是否都可用。

if #available(iOS 9, OSX 10.10, *) {
print("iOS 9+ or OS X 10.10+")      // 条件是或的关系,有一个平台满足就可以,表示指定了在iOS系统上,if段的代码仅会在iOS 9及更高版本的系统上执行;在OS X,仅会在OS X v10.10及更高版本的系统上执行。
} else {
print("old version")
}


可用性条件指定了在iOS系统上,if段的代码仅会在iOS 9及更高版本的系统上执行;在OS X,仅会在OS X v10.10及更高版本的系统上执行。上面的最后一个参数
*
是必须写的,用于处理未来潜在的平台。可用性条件获取了一系列平台名字和版本。平台名字可以是
iOS
OSX
watchOS
。除了特定的主板本号像iOS 8,我们可以指定较小的版本号像iOS 8.3以及OS X v10.10.3。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  swift iOS