使用 if 和 switch 来进行条件操作,使用 for-in、while 和 repeat-while 来进行循环。包裹条件和循环变量的括号可以省略,但是语句体的大括号是必须的。
let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
if score > 50 {
teamScore += 3
} else {
teamScore += 1
}
}
print(teamScore)
在 if 语句中,条件必须是一个布尔表达式——这意味着像 if score { ... } 这样的代码将报错,而不会隐形地与 0 做对比。
你可以一起使用 if 和 let 一起来处理值缺失的情况。这些值可由可选值来代表。一个可选的值是一个具体的值或者是 nil 以表示值缺失。在类型后面加一个问号(?)来标记这个变量的值是可选的。
var optionalString: String? = "Hello"
print(optionalString == nil)
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
}
let nickName: String? = nil
let fullName: String = "John Appleseed"
let informalGreeting = "Hi \(nickName ?? fullName)"
你还可以使用较短的代码解包一个值,并且对该被包装值使用相同的名称。
if let nickname {
print("Hey, \(nickName)")
}
switch 支持任意类型的数据以及各种比较操作——不仅仅是整数以及测试相等。
let vegetable = "red pepper"
switch vegetable {
case "celery":
print("Add some raisins and make ants on a log.")
case "cucumber", "watercress":
print("That would make a good tea sandwich.")
case let x where x.hasSuffix("pepper"):
print("Is it a spicy \(x)?")
default:
print("Everything tastes good in soup.")
}
练习
删除 default 语句,看看会有什么错误?
注意 let 在上述例子的等式中是如何使用的,它将匹配等式的值赋给常量 x。
运行 switch 中匹配到的 case 语句之后,程序会退出 switch 语句,并不会继续向下运行,所以不需要在每个子句结尾写 break。
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (_, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
}
}
}
print(largest)
// 输出 "25"
练习
将 _ 替换成变量名,以确定哪种类型的值是最大的。
使用 while 来重复运行一段代码直到条件改变。循环条件也可以在结尾,保证能至少循环一次。
var n = 2
while n < 100 {
n *= 2
}
print(n)
var m = 2
repeat {
m *= 2
} while m < 100
print(m)
你可以在循环中使用 ..< 来表示下标范围。
var total = 0
for i in 0..<4 {
total += i
}
print(total)
func greet(_ person: String, on day: String) -> String {
return "Hello \(person), today is \(day)."
}
greet("John", on: "Wednesday")
使用元组来生成复合值,比如让一个函数返回多个值。该元组的元素可以用名称或数字来获取。
func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) {
var min = scores[0]
var max = scores[0]
var sum = 0
for score in scores {
if score > max {
max = score
} else if score < min {
min = score
}
sum += score
}
return (min, max, sum)
}
let statistics = calculateStatistics(scores:[5, 3, 100, 3, 9])
print(statistics.sum)
print(statistics.2)
class NamedShape {
var numberOfSides: Int = 0
var name: String
init(name: String) {
self.name = name
}
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
注意 self 被用来区别实例变量 name 和构造器的参数 name。当你创建实例的时候,像传入函数参数一样给类传入构造器的参数。每个属性都需要赋值——无论是通过声明(就像 numberOfSides)还是通过构造器(就像 name)。
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength
枚举和结构体
使用 enum 来创建一个枚举。就像类和其他所有命名类型一样,枚举可以包含方法。
enum Rank: Int {
case ace = 1
case two, three, four, five, six, seven, eight, nine, ten
case jack, queen, king
func simpleDescription() -> String {
switch self {
case .ace:
return "ace"
case .jack:
return "jack"
case .queen:
return "queen"
case .king:
return "king"
default:
return String(self.rawValue)
}
}
}
let ace = Rank.ace
let aceRawValue = ace.rawValue
enum ServerResponse {
case result(String, String)
case failure(String)
}
let success = ServerResponse.result("6:00 am", "8:09 pm")
let failure = ServerResponse.failure("Out of cheese.")
switch success {
case let .result(sunrise, sunset):
print("Sunrise is at \(sunrise) and sunset is at \(sunset)")
case let .failure(message):
print("Failure... \(message)")
}
使用 async let 来调用异步函数,并让其与其它异步函数并行运行。 使用 await 以使用该异步函数返回的值。
func connectUser(to server: String) async {
async let userID = fetchUserID(from: server)
async let username = fetchUsername(from: server)
let greeting = await "Hello \(username), user ID \(userID)"
print(greeting)
}
使用 Task 从同步代码中调用异步函数且不等待它们返回结果
Task {
await connectUser(to: "primary")
}
//Prints "Hello Guest, user ID 97"
协议和扩展
使用 protocol 来声明一个协议。
protocol ExampleProtocol {
var simpleDescription: String { get }
mutating func adjust()
}
类、枚举和结构体都可以遵循协议。
class SimpleClass: ExampleProtocol {
var simpleDescription: String = "A very simple class."
var anotherProperty: Int = 69105
func adjust() {
simpleDescription += " Now 100% adjusted."
}
}
var a = SimpleClass()
a.adjust()
let aDescription = a.simpleDescription
struct SimpleStructure: ExampleProtocol {
var simpleDescription: String = "A simple structure"
mutating func adjust() {
simpleDescription += " (adjusted)"
}
}
var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription
do {
let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")
print(printerResponse)
} catch {
print(error)
}
练习
将 printer name 改为 "Never Has Toner" 使 send(job:toPrinter:) 函数抛出错误。
可以使用多个 catch 块来处理特定的错误。参照 switch 中的 case 风格来写 catch。
do {
let printerResponse = try send(job: 1440, toPrinter: "Gutenberg")
print(printerResponse)
} catch PrinterError.onFire {
print("I'll just put this over here, with the rest of the fire.")
} catch let printerError as PrinterError {
print("Printer error: \(printerError).")
} catch {
print(error)
}
练习
在 do 代码块中添加抛出错误的代码。你需要抛出哪种错误来使第一个 catch 块进行接收?怎么使第二个和第三个 catch 进行接收呢?
var fridgeIsOpen = false
let fridgeContent = ["milk", "eggs", "leftovers"]
func fridgeContains(_ food: String) -> Bool {
fridgeIsOpen = true
defer {
fridgeIsOpen = false
}
let result = fridgeContent.contains(food)
return result
}
fridgeContains("banana")
print(fridgeIsOpen)
泛型
在尖括号里写一个名字来创建一个泛型函数或者类型。
func makeArray<Item>(repeating item: Item, numberOfTimes: Int) -> [Item] {
var result: [Item] = []
for _ in 0..<numberOfTimes {
result.append(item)
}
return result
}
makeArray(repeating: "knock", numberOfTimes: 4)
你也可以创建泛型函数、方法、类、枚举和结构体。
// 重新实现 Swift 标准库中的可选类型
enum OptionalValue<Wrapped> {
case none
case some(Wrapped)
}
var possibleInteger: OptionalValue<Int> = .none
possibleInteger = .some(100)
在类型名后面使用 where 来指定对类型的一系列需求,比如,限定类型实现某一个协议,限定两个类型是相同的,或者限定某个类必须有一个特定的父类。
func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> Bool
where T.Element: Equatable, T.Element == U.Element
{
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
return true
}
}
}
return false
}
anyCommonElements([1, 2, 3], [3])