控制流

For-In循环

可以用来遍历区间

//遍历一个闭区间
//此时无需声明index
for index in 1...5 {
    print("(index) times 5 is (index * 5)")
}

index在每次遍历开始时是会自动赋值的常量,会隐式声明,不用使用关键字let进行声明

有时我们进行遍历,并不关心遍历出的值,只需要遍历的次数,这时候我们可以使用下划线_代替变量名忽略这个值

//求 3的10次方 这时候我们不关心便利出来的值 只关系执行次数
let base = 3
let power = 10
var answer = 1
for _ in 1...power {
    answer *= base
}

有时可能需要间隔式的数字:

let minuteInterval = 5
//stride函数 返回一个间隔的数据
for tickMark in stride(from: 0, to: minutes, by: minuteInterval) {
    // 每5分钟渲染一个刻度线(0, 5, 10, 15 ... 45, 50, 55)
}

我们还可以使用for-in遍历

  • 数组所有元素
  • 遍历一个字典访问键值对,键值对以元组形式返回,可以使用显示的常量名称解读(key, value)
  • 字符串中的字符(greeting.characters.indices,返回包含所有字符的range)

While

While

循环开始时判断条件是否符合,ture执行循环的语句,false结束循环

while square < finalSquare {
    
}

Repeat-While

while的区别就是执行判断条件之前会首先执行一次循环的代码块(类似do-while)

repeat {
    statements
} while condition

条件语句

if语句

let temperatureInFahrenheit = 90
if temperatureInFahrenheit <= 32 {
    print("It's very cold. Consider wearing a scarf.")
} else if temperatureInFahrenheit >= 86 {
    print("It's really warm. Don't forget to wear sunscreen.")
} else {
    print("It's not that cold. Wear a t-shirt.")
}
//也可以不要最后的else语句 这样有可能既不冷也不热,不会触发if和else if 也就不会打印任何消息

switch

将某个值与一个或多个同类型的值作比较

switch some value to consider {
case value 1:
    respond to value 1
case value 2,
    value 3:
    respond to value 2 or 3
default:
    otherwise, do something else
}

完备性(switch必须覆盖所有可能的字符):

对于switch语句每个可能的值,需要至少一个case分支与之对应,对于某些没办法用case覆盖所有分支的情况,可以通过在最后加上default分支来涵盖其他没有对应的值

隐式break

与OC中的switch语句不同,即使在case语句后没有break 也在执行case分钟的代码执行完毕后终止switch语句的执行,不会继续执行下一个case分支,并不存在隐式的贯穿

注意

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

每个case分支中都至少包含一条语句

//case分支后至少需要一条语句 下面这种写法是错误的
let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a": // 无效,这个分支下面没有语句
case "A":
    print("The letter A")
default:
    print("Not the letter A")
}
//此时如果想case"a"分支什么都不执行 可以直接执行一条break语句

此时如果我们想"a"和"A"都执行print("The letter A")可以将两个值组合为一个复合匹配

let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a", "A":  //用逗号隔开
    print("The letter A")
default:
    print("Not the letter A")
}
//为了可读性 可以写为多行形式
case "a",
     "A"

区间匹配

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).")

元组

可以使用元组在同一个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"
//(0,0)虽然符合所有分支模式 但是只有第一个case分支会执行

注意:

与OC中的switch不同,swift中的case分支中的值是允许交叉的,即允许多个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"
let somesPoint = (1, 1)
switch somesPoint {
case (let x, 0):
    print("X中上\(x)")
case (0, var y):
    print("Y中上\(y)")
case let defaultPint:
    print(defaultPint, defaultPint.0, defaultPint.1)
}

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 someCharacter: 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"
let stillAnotherPoint = (9, 0)
switch stillAnotherPoint {
case (let distance, 0), (0, let distance):
    print("On an axis, \(distance) from the origin")
default:
    print("Not on an axis")
}
/**
    第一个case分之中,两个模式都值绑定了distance
*/

控制转移

控制转移语句改变代码执行顺序,实现代码的跳转

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) // 输出 "grtmndsthnklk"
//这里把continue换位break输出结果是一样的,因为continue会匹配为循环体,而break会匹配为switch语句结束执行,循环体还会继续执行下去

Break

立刻结束控制流的执行,可以更早结束一个switch代码块或者一个循环体

循环中的break

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

Switch中的break

Switch中使用break时会立刻中断switch代码块的执行,跳转到表示 switch 代码块
结束的大括号( } )后的第一行代码

我们可以用其去忽略一个或多个分支,因为switch不允许有空分支,我们可以在分支语句中写为break,来立即结束市场代码块的执行

贯穿 fallthrough

Swift与C不同,switch不会从上一个case分支落入到下一个case分支中。
但是如果我们想要与C类似的贯穿的风格就需要加上关键字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"
    fallthrough
case 10:
    description += " , and also"
    fallthrough
default:
    description += " an integer."
}
print(description)
// 输出 "The number 5 is a prime number, and also an integer."

在分支语句加上fallthrough关键字后,在执行完分支语句后会继续执行下面的分支,即贯穿到default分支

注意:

在加上fallthrough后是不会判断下面的case语句的匹配条件而直接执行其分支中的代码

在C语言的switch语句中也是如果不加break时,那么执行完一条case语句后会直接执行下一条case分支代码,而不会判断分支条件,直到遇到break或者switch语句结束,这与swift中是一致的

带标签的语句

当有循环体和条件语句嵌套时,我们想要显式指明用break终止哪个循环体
当有多个循环体嵌套时,显示指明用continue开始哪个循环体的下次循环

这时候就需要 标签

声明一个带标签的语句是通过在该语句的关键词的同一行前面放置一个标签,作为这个语句的前导关键字,该标签后面加上一个冒号。
label name : while condition { statements } //对while循环体的标签

gameLoop: while square != finalSquare {
    diceRoll += 1
    if diceRoll == 7 { diceRoll = 1 }
    switch square + diceRoll {
    case finalSquare:
        // 骰子数刚好使玩家移动到最终的方格里,游戏结束。
        break gameLoop
    case let newSquare where newSquare > finalSquare:
        // 骰子数将会使玩家的移动超出最后的方格,那么这种移动是不合法的,玩家需要重新掷骰子
        continue gameLoop
    default:
        // 合法移动,做正常的处理
        square += diceRoll
        square += board[square]
    }
}
print("Game over!")

我们用标签+冒号+while循环来声明循环为gameLoop,然后用break 标签或者continue 标签来避免break switch语句 而直接作用于循环体

提前退出 guard

if语句类似guard语句的执行取决于其后的一个bool条件,如果为真时直接执行guard语句后的代码,如果为假时执行else中的语句

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

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(person: ["name": "John"])
// 输出 "Hello John!"
// 输出 "I hope the weather is nice near you."
greet(person: ["name": "Jane", "location": "Cupertino"])
// 输出 "Hello Jane!"
// 输出 "I hope the weather is nice in Cupertino."

检测API的可用性

Swift内置支持检查API可用性,可以确保我们不会在当前机器上使用不可用API,否则会在编译期间报错

为了判断代码在指定部署机器上是否可用,我们在ifguard中使用可用性条件去执行一段代码,在运行时有条件的执行一段代码,来在运行时判断调用的API是否可用

if #available(iOS 10, macOS 10.12, *) {
    // 在 iOS 使用 iOS 10 的 API, 在 macOS 使用 macOS 10.12 的 API
} else {
    // 使用先前版本的 iOS 和 macOS 的 API
}

最后的 * 是必须的用于指定在所有其它平台中,如果版本号高于你的设备指定的最低版本,if语句的代码块将会运行。

#available()一般使用平台名字(iOS,macOS,watchOS,tvOS)以及版本号(iOS 10 等)