swift里面的控制流分为For-In 循环、While 循环、 条件语句、控制转移语句、 提前退出、检测 API 可用性
For-in循环
相对于OC,Swift对for-in做出了加强,不仅仅能对数组遍历,还能对字符串和字典遍历,范围可以使用闭区间,item值可以用(_)进行忽略,还加入了stride(from:to:by:)、stride(from:through:by:) 等新API
遍历字符串
let name = "Logan"
for char in name {
print(char)
}
//L
//o
//g
//a
//n
遍历数组
let names = ["zhangsan", "lisi", "wangwu"]
for name in names {
print(name)
}
// zhangsan
// lisi
// wangwu
遍历字典
- 通过遍历字典来访问它的键值对。遍历时,字典的每项元素会已
(key, value)元祖的形式返回
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 index in 1...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
忽略变量遍历
- 当不需要区间序列内的每一项值,可以用下划线(
_)替代变量名来忽略这个值,如下:
let base = 3
let power = 10
var answer = 1
for _ in 1...power {
answer *= base
}
print("\(base) to the power of \(power) is \(answer)")
// 输出 "3 to the power of 10 is 59049"
跨越遍历
- 当只需要取被遍历集合中的部分数据是可以使用跨越遍历(自己取的名字- -),方法名为:
stride(from:to:by:)、stride(from:through:by:)如下:
let minutes = 60
let minuteInterval = 5
for tickMark in stride(from: 0, to: minutes, by: minuteInterval) {
// 每5分钟渲染一个刻度线(0, 5, 10, 15 ... 45, 50, 55)
}
let hours = 12
let hourInterval = 3
for tickMark in stride(from: 3, through: hours, by: hourInterval) {
// 每3小时渲染一个刻度线(3, 6, 9, 12)
}
可以这么理解: to不包含最后一个值,through包含最后一个值
While循环
while循环类似OC的用法,一直运行到条件变成false。而swift提供两种循环形式:while和repeat-while(每次在循环结束后检查条件是否符合,类似OC的do-while)
while
- 每次循环开始前检查条件是否符合(先判断再循环)如下:
var condition = 0
while condition < 3 {
condition += 1
print(condition)
}
// 1
// 2
// 3
repeat-while
- 每次在循环结束后检查条件是否符合(先循环然后再判断,类似OC的
do-while)如下:
var condition = 3
repeat {
condition += 1
print(condition)
} while condition < 3
// 4
条件语句
swift提供两种条件语句if和switch
if条件语句
- 先判断if后面的语句是否为
true,然后执行相关代码。if语句允许二选一执行,叫做else从句。也就是当条件为false时,执行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.")
}
// 输出 "It's not that cold. Wear a t-shirt."
switch条件语句
switch语句会尝试把某个值与若干个模式进行匹配。根据第一个匹配成功的模式,switch语句会执行对应的代码。当有可能的情况较多时,通常switch语句替换if语句。每一个case都是代码执行的一条分支。switch语句会决定哪一条分支应该被执行。这个流程被称作根据给定的值切换(switching),switch语句必须完备,每一个可能的值都必须至少有一个case分支与之对应。当某些不能涵盖所有值得情况下,可以使用默认default分支来涵盖其它所有没有对应的值如下:
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")
}
// 输出 "The last letter of the alphabet"
不存在隐式的贯穿
- 与C和OC中的
switch语句不同,在swift中,当匹配的case分支中的代码执行完毕后,程序会终止switch语句,而不会继续执行下一个case分支。也就是说,不需要在case分支中使用break。所以分支下必须要有执行语句,不然会编译错误如下:
let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a": // 无效,这个分支下面没有语句
case "A":
print("The letter A")
default:
print("Not the letter A")
}
// 这段代码会报编译错误
为了让单个 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
switch区间分配
- case分支的模式也可以是一个值的区间,使用区间匹配来输出任意数字对应的自然语言格式如下:
let approximateCount = 62
let countedThings = "moons orbiting Saturn"
let 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-元祖
- 我们还可以使用元祖在同一个
switch语句中测试多个值,元祖中的元素可以是值,也可以是区间。可以使用下划线(_)来匹配所有可能的值。使用一个(Int, Int)类型的元祖来分类下图中的点 (x,y) 如下:
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")
}
// 输出 "(1, 1) is inside the box"
注: 不像OC,swift允许多个case匹配同一个值。实际上,如果 somePoint = (0, 0),则可以匹配上述的四个case。但是存在多个匹配,那么只会执行第一个被匹配到的case分支。考虑 *(0,0)*会首先匹配case(0,0),因而身下的能够匹配的分支都会被忽视掉。
绑定值
- 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))")
}
// 输出 "on the x-axis with an x value of 2"
注: 绑定值只作用于switch对应的case内。上面例子中的三个case都声明了常量x和y的占位符,用于临时获取元祖anotherPoint的一个或两个值。第一个case *case(let x,0)*将匹配一个纵坐标为0的点,并吧这个点的横坐标赋值给临时常量x。类似的第二个case *case(0,let y)*将匹配一个横坐标为0的点,并把这个点的纵坐标赋值给临时常量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"
注: 上述代码中的where实质相当于case分支中的额外判定条件生产的一个过滤器,当where语句的条件为true时,匹配到的case分支才会被执行。
控制转移语句
Swift中的控制转移语句共有下面五种: continue、break、fallthrough、return、throw。
Continue
continue语句告诉循环体立即停止本次循环,重新开始下次循环。就好像再说“本次循环我已经执行完了”,但是并不会离开整个循环体。举例把一个小写字符串的元音字母和空格字符移除,生成一个含义模糊的短句如下:
let puzzleInput = "great minds think alike"
var puzzleOutput = ""
for character in puzzleInput {
switch character {
case "a", "e", "i", "o", "u", " ":
continue
default:
puzzleOutput.append(character)
}
}
print(puzzleOutput)
// 输出 "grtmndsthnklk"
Break
break语句会立刻结束整个控制流的执行,然后跳转到表示循环体结束的大括号后的第一行代码,不会再有本次循环的代码被执行,也不会有下次的循环产生。在switch中基本不用,可以在if循环语句中来提前结束,避免过多的遍历次数,举例如下:
let students = [["name":"xiaoming", "height":155], ["name":"xiaohua", "height":160], ["name":"xiaoli", "height":172]]
for student in students {
if student["height"] as! Int >= 160 {
print("There have students who are taller than 160")
break
}
}
// There have students who are taller than 160 *(只执行一次)
fallthrough贯穿
- 在Swift里,
switch语句不会从上一个分支跳转到下一个分支中,只要匹配到相应的分支,就会去完成分支下的执行语句,整个switch代码块就会完成。加入fallthrough后就会继续执行下个case分支,举例如下:
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)
// 输出 "The number 5 is a prime number, and also an integer."
注:fallthrough关键字不会检查下一个执行条件的case中的匹配条件。而是简单粗暴的继续连接到下个case分支的执行代码中。
提前退出
- 像
if语句一样,guard的执行取决于一个表达式的布尔值。我们可以使用guard语句来要求条件必须为真时,以执行guard代码块后的代码。不同于if语句,一个guard语句只有一个else从句,如果条件不为真则执行else从句中的代码。可以理解为guard实际是省略了if条件下执行代码块而只有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(["name": "John"])
// 输出 "Hello John!"
// 输出 "I hope the weather is nice near you."
greet(["name": "Jane", "location": "Cupertino"])
// 输出 "Hello Jane!"
// 输出 "I hope the weather is nice in Cupertino."
注:else这个分支必须转移控制以退出 guard 语句出现的代码段。它可以用控制转移语句如 return,break,continue 或者 throw 做这件事,或者调用一个不返回的方法或函数,例如 fatalError()。
检测 API 可用性
- Swift 内置支持检查 API 可用性,这可以确保我们不会在当前部署机器上,不小心地使用了不可用的 API。
编译器使用 SDK 中的可用信息来验证我们的代码中使用的所有 API 在项目指定的部署目标上是否可用。如果我们尝试使用一个不可用的 API,Swift 会在编译时报错。
我们在
if或guard语句中使用可用性条件(availability condition)去有条件的执行一段代码,来在运行时判断调用的 API 是否可用。编译器使用从可用性条件语句中获取的信息去验证,在这个代码块中调用的 API 是否可用。举例如下:
if #available(iOS 10, macOS 10.12, *) {
// 在 iOS 使用 iOS 10 的 API, 在 macOS 使用 macOS 10.12 的 API
} else {
// 使用先前版本的 iOS 和 macOS 的 API
}