Swift 学习- 06 -- 控制流

// 控制流

// swift 提供了多种控制流结构,包括可以多次执行的 while 循环,基于特定条件选择执行不同分支的 if, guard 和 switch 语句,还有控制流程跳转到其它代码位置的 break 和 continue 语句

// swift 还提供了了 for-in 循环,用来更简单地遍历数组(array), 字典(dictionary), 区间(range), 字符串(String) 和其它序列类型

// swift 的switch 语句比C语言中更加强大, 在C语言中,如果某个 case 不小心漏写了 break, 这个 case 就会贯穿至下一个 case swift 无需写 case ,也无需写 break 所以不会发生贯穿的情况, case 还可以匹配很多不同的模式,包括间隔匹配, 元组(tuple) 和转换到特定类型. swift 语句的case 中匹配的值还可以绑定成临时变量或常量, 在 case 中使用, 也可以用 where 来描述更加复杂的匹配条件

// for-In 循环

// 你可以使用 for-in 循环来遍历一个集合中的所有元素, 例如数字范围,数组中的元素或者字符串中的字符

for index in 1...5 {

print("\(index) times 5 is \(index*5)")

}

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

// 如果你不需要区间序列内每一项的值, 你可以使用 (_) 替代变量名来忽略这个值

// (_)(替代循环的变量) 能够忽略当前值, 并且不提供循环遍历时对值的访问

let names = ["Anna","Alex","Brian","Jack"]

for name in names {

print("Hello, \(names)")

}

// 你也可以通过遍历一个字典来访问它的键值对,遍历字典时候.字典的每项元素会以 (key, value) 元组的形式返回, 你可以在 for-in 循环中使用显式的常量名称来解读 (key, value) 元组

let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]

for (animalName, legCount) in numberOfLegs {

print("\(animalName)s have \(legCount) legs")

}

// 字典元素的遍历顺序和插入顺序可能不同, 字典的内容在内部是无序的, 所以遍历元素时不能保证顺序

// While

// While 循环从计算一个条件开始, 如果条件为 true ,会重复运行一段语句, 直到条件变为 false, 才会结束跳过

// Repeat-While

// While 循环的另一种形式, 它和 while 的区别是在判断循环条件之前 ,先执行一次循环的代码块, 然后重复循环知道条件 false

// Swift 语言的 repeat-while 循环和其它语言的 do-while 循环是类似的

//repeat{

//

//}while condtion

// 条件语句

// 根据特定条件执行特定的代码通常是十分有用的,当错误发生时, 你可能想运行额外的代码, 或者,当值太大或者太小的时候,向用户显示一条消息, 要实现这些功能, 你就需要使用条件语句

// swift 提供两种类型的条件语句, if 语句 和 switch 语句, 通常,当条件较为简单而且可能 的情况横少时 , 使用 if 语句, 而 switch 语句更适用于条件比较复杂的,有更多排列组合的时候, 并且在 switch 在需要用到模式匹配的情况下回更有用

// If

// if 语句最简单的形式就是 只包含一个条件 ,当改条件为 true 的时候, 才会执行相关代码

var temperatureInFahrenHeit = 30

if temperatureInFahrenHeit <= 32 {

print("It's very cold, Consider wearing a scarf")

}

// 当然, if 语句允许二选一执行, 叫做 else 从句, 也就是当条件为 FALSE 时 ,执行 else 语句

// Switch

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

// switch 语句最简单的形式就是把某个值与一个或若干个相同类型的值做比较

switch 1 {

case 1:

print(1)

default:

print("no value")

}

// switch 语句由多个 case 构成,每个有 case 关键字开始,为了匹配某些特定的值, swift 提供了几种 方法来进行更复杂的模式匹配,

// 与 if 语句类似, 每一个 case 嗾使代码执行的一条分支, switch 语句会决定哪一条分支应该被执行, 这个流程被称作更具给定的值切换

// switch 语句必须是完备的,这就是说,每一个可能的值都必须至少有一个 case 分支预支对应, 在某些不可能涵盖所有值的情况下, 你可以使用默认 (default) 分支来涵盖其他所有没有对应值的情况, 这个默认分支必须在 switch 语句的最后面

// 例如

let someCharacter: Character = "z"

switch someCharacter {

case "a":

print("The first letter of the alphbet")

case "z":

print("The last letter of the alphabet")

default:

print("Some other character")

}

// 不存在隐式的贯穿

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

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

// 每一个分支 case 都必须包含至少一条语句, 分支下面不能为空

//let anotherCharacter: Character = "a"

//switch anotherCharacter {

//case "a": // 无效,这个分支下面没有语句

//case "A":

// print("The letter A")

//default:

// print("Not the letter A")

//}

//// 这段代码会报编译错误

// 不像 C语言里的 switch 语句,在swift 中, switch 语句 不会一起匹配 "a" 和 "A", 相反的,上面的代码会引起编辑器错误, case "a": 不包含任何可执行语句, 这就避免了意外地从一个case 分支贯穿到另外一个, 使得代码更加安全, 也更直观

// 为了让单个 case 同时匹配 a 和 A ,可以将这两个值合成一个复合匹配,并且用逗号隔开:

let anotherCharacter: Character = "a"

switch anotherCharacter {

case "a", "A":

print("The letter A")

default:

print("Not the letter A")

}

// 输出 "The letter A

// 注意 : 如果想要显式贯穿 case 分支, 请使用 fallthrough 语句

// 区间匹配

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

let approximateCount = 62

let countedThings = "moons orbiting Saturn"

var naturalCount: String

switch approximateCount {

case 0:

naturalCount = "no"

case 1..<5:

naturalCount = "a few"

case 5..<12:

naturalCount = "several"

case 12..<100:

naturalCount = "dozens of"

case 100..<1000:

naturalCount = "hundreds of"

default:

naturalCount = "many"

}

print("There are \(naturalCount) \(countedThings).")

// 输出 "There are dozens of moons orbiting Saturn."

// 元组

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

let somePoint = (1, 1)

switch somePoint {

case (0, 0):

print("(0, 0) is at the origin")

case (_, 0):

print("(\(somePoint.0), 0) is on the x-axis")

case (0, _):

print("(0, \(somePoint.1)) is on the y-axis")

case (-2...2, -2...2):

print("(\(somePoint.0), \(somePoint.1)) is inside the box")

default:

print("(\(somePoint.0), \(somePoint.1)) is outside of the box")

}

// 输出 "(1, 1) is inside the box"

// 值绑定 (Value Bindings)

// case 分支允许将匹配的值绑定到一个临时的常量或变量, 并且在 case 分支体内使用 -- 这种行为称为值绑定, 因为匹配的值在 case 分支体内, 与临时的常量或变量绑定

let anotherPoint = (2, 0)

switch anotherPoint {

case (let x, 0):

print("on the x-axis with an x value of \(x)")

case (0, let y):

print("on the y-axis with a y value of \(y)")

case let (x, y):

print("somewhere else at (\(x), \(y))")

}

// Where

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

let yetAnotherPoint = (1, -1)

switch yetAnotherPoint {

case let (x, y) where x == y:

print("(\(x), \(y)) is on the line x == y")

case let (x, y) where x == -y:

print("(\(x), \(y)) is on the line x == -y")

case let (x, y):

print("(\(x), \(y)) is just some arbitrary point")

}

// 输出 "(1, -1) is on the line x == -y"

// 复合匹配

// 当多个条件可以使用同一种方法来处理时候,可以将这几种可能放在同一个 case 后面, 并且用逗号隔开, 当 case 后面的任意一种模式匹配的时候, 这条分支就会被匹配,并且,如果匹配列表过长,还可以分行书写

let somesCharacter: 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")

}

// 输出 "e is a vowel"

// 复合匹配同样可以包含绑定值, 复合匹配里面所有的匹配模式, 都必须包含相同的的值绑定,并且没一个绑定都必须获取到相同类型的值,这保证了, 无论复合匹配中的那个模式发生了匹配,分支体内的代码,都能获取到绑定的值,并且绑定的值都有一样的类型

// 控制转移语句

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

// continne

// break

// fallthrough

// return

// throw

// continue

// continue 告诉一个循环立即停止本次循环,重新开始下次循环,就好像说 '本次循环我已经执行完了', 但是并不会离开整个循环体

let puzzleInput = "great minds think alike"

var puzzleOutput = ""

for character in puzzleInput.characters {

switch character {

case "a", "e", "i", "o", "u", " ":

continue

default:

puzzleOutput.append(character)

}

}

print(puzzleOutput)

// break

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

// 循环语句中的 break

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

// Switch 中的 break

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

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

// 注意 : 当一个 switch 分支紧紧包含注释时, 会被编辑时错误, 注释不是代码语句而且也不能让 switch 分支达到被忽略的效果, 应该使用 break来忽略某个分支

// 贯穿

// swift 中的 switch 不会从上一个 case 分支落入到下一个 case 分支中, 相反,只要第一个匹配到的 case 分支完成了它需要执行的语句,整个 switch 代码块完成了它的执行, 相比之下, C语言要求你显式地插入 break 语句到每个 case 的结尾来阻止自动落入到 下一个 case 分支中, swift 的这种避免落入下一个分支的特性一位整个他的 switch 功能要比 C语言 的更加的清晰和可预测, 可以避免无意识执行多个 case 分支从而引发的错误

// 如果你确实需要 C语言的贯穿的特性, 你可以在每个需要改特性的 case 分支中使用 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)

// 注意 : fallthrough 关键字不会检查下一个将会落入执行的 case 分支的匹配条件, fallthrough 鸡蛋地使得代码继续连接到下一个 case 中的代码, 这个和 C语言标准 switch 语句特性是一样的

// 带标签的语句

// 在 swift 中. 你可以在循环体和条件语句中嵌套循环体和添加语句来创造复杂的控制流结构, 并且,循环体和条件语句都可以使用 break 语句来提前结束整个代码块

// 为了实现这个目的, 你可以使用标签 (statement label) 来标记一个循环体或者条件语句, 对于一个条件语句, 你可以使用 break 加标签的方式,来结束这个被标记的语句, 对于一个循环语句,你可以使用 break 或者 continue 加标签吗来结束或者继续这条被标记语句的执行

// 声明一个带标签的语句是通过在该语句的关键字的同一行前面放置一个标签, 作为这个语句的前导关键字 ,并且该标签后面跟随一个冒号, 下面的是一个 while 循环体的标签语法,同样的规则适用于所有的循环体和条件语句

// 提前退出

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

// 如果 guard 语句的条件被满足, 则执行 guard 语句大括号后面的代码, 将变量或者常量的可选绑定 guard 语句的条件 ,都可以保护 guard 语句后面的代码

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

// 相比于可以实现同样功能的 if 语句, 按需使用 guard 语句会提升我们代码的可读性,它可以使你的代码连贯的被执行而不需要将它包在 else 块中, 它可以使你在紧邻条件判断的地方,吗处理违规的情况

// 检测 API 的可用性

// swift 内置支持检查 API 可用性, 这可以确保我们不会再当前部署的机器上, 不小心使用了不可用的 API

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

// 我们在 if 或 guard 语句中使用 可用性条件 (availability condition) 去有条件的执行一段代码, 来运行时判断调用的 API 是否可用, 编辑器使用从可用性条件语句中获取的信息去验证,在这个代码块中是可用

if #available(iOS 10, macOS 10.12, *) {

}else{

}

// 以上可用性条件指定, 在 iOS 中, if 语句的代码块仅仅在 iOS 10 以及更高的系统下运行; 在 MacOS 中, 仅在 macOS 10.12 以及更高的版本才会运行, 最后一个参数 (*) 是必须的, 用于指定在所有其它平台中, 如果版本高于你的设备指定的最低版本, if 语句的代码块也会执行

// 在它一般的形式中, 可用性条件使用了一个平台名字和版本的列表, 平台名字可以是 iOS, macOS, watchOS 和 tvOS , 除了指定像 iOS 8 这样的主版本号, 我们可以指定 iOS 8.3 以及 macOS 10.10.3 的子版本号