Swift--控制流

Posted 一人前行

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Swift--控制流相关的知识,希望对你有一定的参考价值。

Swift中提供了一系列的控流状态,其中包括可以多次执行相同任务的while循环、根据不同的条件执行不同分支的if guard switch 等语句,以及例如用break和continue来选择是否跳出循环执行其他的代码。

 

Swift也提供对数组、字典、范围、字符串等快速遍历的方法:for-in循环。

 

Swift中的swift语句比类似于c语言中的功能要强大一些,Case语句可以适合多种不同的类型,例如间隔匹配、元组、或者一个特殊的类型。在switch case中,匹配的值可以绑定到临时常量或变量,在case的主体中使用,复杂的匹配条件可以用where子句表示。

 

1. for-in循环

可以用for-in来循环遍历数组、字典、字符串中的字符等。

let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names {
    print("Hello, \(name)!")
}
// Hello, Anna!
// Hello, Alex!
// Hello, Brian!
// Hello, Jack!

 你可以用for-in遍历字典来获取到字典中的键值对。字典中的每个item以(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-in循环来遍历数值范围。

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

 

遍历的序列是一个从1到5的数字范围,包括使用闭域运算符(…)。将索引值设置为range(1)中的第一个数字,并执行循环中的语句。在这种情况下,循环只包含一个语句,该语句从5倍的表中输出当前索引值的条目。在执行语句之后,将更新index的值,以包含range(2)中的第二个值,以及打印(_:分隔符:terminator:)函数再次调用。这个过程一直持续到到达距离的末端。

 

在上面的例子中,index是一个常量,它的值是在循环的每次迭代开始时自动设置的。因此,索引不需要在使用前声明。它被隐式地声明为简单地由它包含在循环声明中,而不需要一个let声明关键字。

 

如果您不需要从序列中获得每个值,则可以使用一个下划线代替变量名来忽略值。

let base = 3
let power = 10
var answer = 1
for _ in 1...power {
    answer *= base
}
print("\(base) to the power of \(power) is \(answer)")
// Prints "3 to the power of 10 is 59049"

 

在一些情况下,你可能不想要闭区间(包括两边的值)。考虑一下在表盘上画一分钟的秒表刻度值,如果想画60下,需要从0开始,请用半开区别操作符(..<)--其中包含最小值,但是不包含最大值。

let minutes = 60
for tickMark in 0..<minutes {
    // render the tick mark each minute (60 times)
}

 有些用户想让他们的界面用更少的刻度时,他们可能5秒刻度一次。用 stride(from:to:by:)的方法跳过不想要的刻度值。

let minuteInterval = 5
for tickMark in stride(from: 0, to: minutes, by: minuteInterval) {
    // render the tick mark every 5 minutes (0, 5, 10, 15 ... 45, 50, 55)
}

 闭区间也可以用stride(from:through:by)来代替:

let hours = 12
let hourInterval = 3
for tickMark in stride(from: 3, through: hours, by: hourInterval) {
    // render the tick mark every 3 hours (3, 6, 9, 12)
}

 

2. While循环

While循环根据判断是否满足条件开始的,if这个条件满足,就执行while内部代码,知道它的条件不满足。

while condition {
    statements
}

 

3.Rapeat-While循环

repeat-while循环是另外一种while循环的版本。在判断条件是否满足的情况下,先执行循环内的代码,如果条件不满足,就继续循环,知道满足条件为止跳出循环。

repeat {
    statements
} while condition

 

4.条件控制语句

对不同的条件执行不同的代码是很常见的。当出现错误的时候你可能需要执行不同的代码或者当一个值太高或者太低时展示一个提示信息。要实现这样的效果,让你的部分代码成为条件语句。

Swift提供两种方法允许在代码添加条件分支:if语句和switch语句。通常,你用if语句来判断一个只有几个可能的简单判断句;switch比较适合判断一些值比较多的复杂条件,并且对匹配不同的条件选择不同的执行代码的情况中是非常有用的。

(1)if

在最近简单的形式中,if语句只有一个简单if条件。当条件为true时,会执行if内部的代码。

var temperatureInFahrenheit = 30
if temperatureInFahrenheit <= 32 {
    print("It‘s very cold. Consider wearing a scarf.")
}
// Prints "It‘s very cold. Consider wearing a scarf."

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.")
}
// Prints "It‘s not that cold. Wear a t-shirt."

 你可以将多个if语句叠加使用,可以考虑额外的字句;

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.")
}
// Prints "It‘s really warm. Don‘t forget to wear sunscreen."

 (2)switch

switch语句可以认为是一个值与多个可能值匹配的过程。当有一个值匹配成功后,就是执行对应的代码块switch为响应多个可选项的if语句提供另外一个选择。

在swith简单格式中,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语句都是有多个case语句组成的,每个case语句以case 关键字开始。除了比较指定的值外,swift还为匹配复杂的case语句提供多种方法。

类似于if语句,每个case语句的分支由执行的代码分割开。switch语句决定选择哪个分支。

 

每个switch语句都应该是详细的。每个可能值的类型都应该符合switch语句中其中一个case的类型。如果不适合为所有可能值提供一个case语句,你可以定一个default的语句去包含任何不明确的值。这个default语句以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")
}
// Prints "The last letter of the alphabet"

 

与c或者oc中的switch不一样的是, 在swift中,switch语句不会自动进入下一个case语句中。相反,一旦switch语句符合某个case语句并执行完代码后,不需要明确写break,switch的case语句也会结束。相比于c中的switch语句更安全、更容易使用,而且容易避免错误。

 

每个case语句下都应有至少一句执行的代码,如下的代码就是无效,因为第一个case是空的:

let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a": // Invalid, the case has an empty body
case "A":
    print("The letter A")
default:
    print("Not the letter A")
}
// This will report a compile-time error.

 与c语言的switch语句不一致,这个switch语句没有同时满足于“a”和“b”的情况。同时,它会在case“a”语句中会报一个编译时错误:case “a”没有包含任何执行的代码。这种方法避免了偶然从一个case条件,进入下一个条件的发生,以及让代码更加安全、意图更加明确。

 

要实现一个符合“a”和“A”的case语句中,把两个值结合在同一个复合的case语句中用“,”分隔开。

let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a", "A":
    print("The letter A")
default:
    print("Not the letter A")
}
// Prints "The letter A"

 为了便于阅读,一个符合语句可以写成多行。

 

(1)间隔匹配

swift 中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).")
// Prints "There are dozens of moons orbiting Saturn."

 

(2)元组

你可以用元组来测试swith语句中的多个值是否相同。每个元组的的元素可是根据不同的值或者不同的间隔值来测试。或者你可以用“_”去匹配所有的可能值。

 

例如下面的例子,取(x, y)的点,用元组(Int, Int)类型表示:

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))")
}
// Prints "on the x-axis with an x value of 2"

 

(3)where

switch语句中可以用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")
}
// Prints "(1, -1) is on the line x == -y"

 

(4)复杂语句

switch多个case中如果case中的代码是相同的,可以把两个条件写在同一个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")
}
// Prints "e is a vowel"

 switch语句的第一个例子与英语中的所有五个小写元音匹配。同样,它的第二种情况匹配所有小写的英语辅音。最后,默认情况匹配任何其他字符。

 

复合语句还可以包括值绑定。复合语句的所有模式都必须包含相同的值绑定集,而且每个绑定都必须从复合语句中的所有模式中获得相同类型的值。这确保了,无论复合语句中的哪一部分匹配,case主体中的代码始终可以访问绑定的值,并且值始终具有相同的类型。

 

5. 控制转移语句

控制转移语句可以改变你代码执行的顺序,通过控制转移语句从一个代码块到另个代码块。Swift有5个控制5个转移语句:

contitue

break

fallthrough

return

throw

本章只介绍前面的三种控制转移语句。

(1)continue

continue这个语句是告诉循环结束当前的代码执行,重新开始下一个循环。相当于说:“一直在重复循环当前的循环”,没有离开这个循环。

接下来的例子是,删除一个小写字符串中的元音字母新成一个新的字符串:

let puzzleInput = "great minds think alike"
var puzzleOutput = ""
let charactersToRemove: [Character] = ["a", "e", "i", "o", "u", " "]
for character in puzzleInput {
    if charactersToRemove.contains(character) {
        continue
    } else {
        puzzleOutput.append(character)
    }
}
print(puzzleOutput)
// Prints "grtmndsthnklk"

 (2)break

break语句可以立刻结束执行的代码块。break语句可以用在switch或者循环语句内,当你想提前结束执行代码的时候。

 

在循环语句中使用break

当你在循环语句中使用break时,break会立刻结束当前的循环代码并跳出该循环从而执行“}”外的代码。当前的循环代码不再执行,也不会重新开始一轮新的循环。

 

在switch语句中用break

当break用在switch分支中时,break的使用可以立刻技术switch分支内的执行代码,并且执行switch外及switch中“}”外的代码。

这个行为可以在匹配或者忽略switch语句中用到。因为Swift中的swith语句是详尽的而且在case语句中不允许有空的代码块,为了你的思路更清晰,有时候认真匹配或者忽略一些case语句是有必要的。当switch中的执行其中复合的case语句时,case语句如果有break,执行到break时那么就会立刻结束当前switch代码。

let numberSymbol: Character = "三"  // Chinese symbol for the number 3
var possibleIntegerValue: Int?
switch numberSymbol {
case "1", "?", "一", "?":
    possibleIntegerValue = 1
case "2", "?", "二", "?":
    possibleIntegerValue = 2
case "3", "?", "三", "?":
    possibleIntegerValue = 3
case "4", "?", "四", "?":
    possibleIntegerValue = 4
default:
    break
}
if let integerValue = possibleIntegerValue {
    print("The integer value of \(numberSymbol) is \(integerValue).")
} else {
    print("An integer value could not be found for \(numberSymbol).")
}
// Prints "The integer value of 三 is 3."

 (3)fallthrough

在swift,swift语句中的每个case不会结束后自动进入下一个case语句判断中,当有个case字句一旦满足条件时这个switch语句就完全被执行了。与之相反的是,在c语言中明确指出需要,在case语句结束后插入break,防止从这个case语句中进入下一个case语句。避免默认执行fallthrough意味着swift中的switch语句比c中的更加一致和可预测性,从而可以避免错误地执行多个switch语句。

如果你需要c类型的fallthrough行为,你可以选择在case与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)
// Prints "The number 5 is a prime number, and also an integer."

 注意:fallthrouht关键的使用时,不会判断下一个case语句的条件,而是直接进入执行下一个case语句的代码块。

6.标签声明

在switf中,你可能在一个循环语句中嵌套一个循环和条件语句从而生成一个复杂的控制条件语句。然而循环和条件语句都可以用break来结束他们的执行。因此,有时候在循环语句中用break语句结束代码是很有用的。同样,如果有多个嵌套循环语句中,在循环语句中用continue 也是很有效的。

 

为了达到这个目标,你可以用标签语句来标记一个循环语句或者条件语句。使用条件语句,可以在语句标签中使用break语句结束标记语句的执行。在循环语句中,你可以在标签语句中用break或者continue语句去结束或者继续标记语句的执行。

 

一个标签语句的声明格式如下:在语句前加上一个label的名字用“:”分隔开。

(label name): while condition {
    statements
}
//()不用写上,此处写上为了提示这个一个整体的名字,并不是label + name

 例子如下:

gameLoop: while square != finalSquare {
    diceRoll += 1
    if diceRoll == 7 { diceRoll = 1 }
    switch square + diceRoll {
    case finalSquare:
        // diceRoll will move us to the final square, so the game is over
        break gameLoop
    case let newSquare where newSquare > finalSquare:
        // diceRoll will move us beyond the final square, so roll again
        continue gameLoop
    default:
        // this is a valid move, so find out its effect
        square += diceRoll
        square += board[square]
    }
}
print("Game over!")

 注意:如上如果使用break语句时没有用gameLoop标签,那么break跳出来的不是while循环而是switch语句。

 

6.提前退出

guard 语句与if语句类似,都是根据一个表达式返回的布尔值来判断是否执行。用gurad语句执行时guard后面代码时必须要求guard语句的判断条件为true。与if语句不一致的是,guard语句中else语句一直都会存在,当条件不满足时,这个case语句就会执行。

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

 如果guard语句中条件满足,就会执行guard之外的代码。任何在guard语句条件判断中声明的变量或者常量,在guard之外的代码都可以使用。

如果条件不满足,会执行code之外的代码分支。在case分支中,你必须让代码结束执行。你可以用break,return,Continue、throw来实现。

 

7.检查API的可行性

Swift的编译是在检查API的可行上进行的,确保你不用真的调用一个在实际执行任务中不能使用的API。

编译器会用SDK有用的信息检查你的应用程序在你的代码用到的所有API。当你使用一个不可用的API时,Swift会提示编译时错误信息。

 

你可以在if或者guard语句中用availability条件,根据这些判断你想在runtime用的API是否可有效,去有条件地执行代码块。当检查API在代码中是有效时,编译器就用这些可行性的条件信息。

if #available(ios 10, macOS 10.12, *) {
    // Use iOS 10 APIs on iOS, and use macOS 10.12 APIs on macOS
} else {
    // Fall back to earlier iOS and macOS APIs
}

 

上面的可用性条件指定在iOS中,if语句的主体只在ios10中执行;在macOS,只有在macOS 10.12和之后。最后一个参数*是必需的,并指定在任何其他平台上,如果执行的是目标指定的最小部署目标。

 

在它的一般形式中,可用性条件包含一个平台名称和版本的列表。您可以使用平台名称(如iOS、macOS、watchOS和tvos)作为完整的列表,参见声明属性。除了指定主要版本号,如iOS 8或macOS 10.10,您还可以指定较小的版本号,如ios8.3和macOS 10.10.3。

if #available(platform name version, ..., *) {
    statements to execute if the APIs are available
} else {
    fallback statements to execute if the APIs are unavailable
}

 

以上是关于Swift--控制流的主要内容,如果未能解决你的问题,请参考以下文章

Swift--控制流

Swift新async/await并发中利用Task防止指定代码片段执行的数据竞争(Data Race)问题

Swift新async/await并发中利用Task防止指定代码片段执行的数据竞争(Data Race)问题

Swift语句参考!

Swift语句参考!

swift菜鸟入门视频教程-05-控制流