Swift5.4 语言指南,三 快速预览

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

➤微信公众号:山青咏芝(shanqingyongzhi)

➤博客园地址:山青咏芝(https://www.cnblogs.com/strengthen/

➤GitHub地址:https://github.com/strengthen/LeetCode

➤原文地址:https://www.cnblogs.com/strengthen/p/9711470.html

➤如果链接不是山青咏芝的博客园地址,则可能是爬取作者的文章。

➤原文已修改更新!强烈建议点击原文地址阅读!支持作者!支持原创!

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

热烈欢迎,请直接点击!!!

进入博主App Store主页,下载使用各个作品!!!

注:博主将坚持每月上线一个新app!!!

传统建议使用新语言的第一个程序应打印“ Hello,world!”字样。屏幕上。在Swift中,这可以单行完成:

  1. print("Hello, world!")
  2. // Prints "Hello, world!"

如果您使用C或Objective-C编写代码,则此语法看起来很熟悉-在Swift中,这行代码是完整的程序。您不需要导入单独的库来实现输入/输出或字符串处理之类的功能。在全局范围内编写的代码用作程序的入口点,因此您不需要main()功能。您也不需要在每个语句的末尾写分号。

本教程通过向您展示如何完成各种编程任务,为您提供了足够的信息来开始在Swift中编写代码。如果您不了解某些内容,请不要担心-本教程的其余部分将详细介绍此导览中介绍的所有内容。

笔记

为了获得最佳体验,请在Xcode中以游乐场的形式打开本章。在操场上,您可以编辑代码清单并立即查看结果。

下载游乐场

简单的价值观

使用let做一个常数,var使一个变量。常量的值在编译时不需要知道,但是您必须为它赋值一次。这意味着您可以使用常量来命名一次确定但在许多地方使用的值。

  1. var myVariable = 42
  2. myVariable = 50
  3. let myConstant = 42

常量或变量的类型必须与要分配给它的值的类型相同。但是,您不必总是显式地编写类型。在创建常量或变量时提供一个值,可使编译器推断其类型。在上面的示例中,编译器将其推断myVariable为整数,因为其初始值为整数。

如果初始值不能提供足够的信息(或者不是初始值),请通过在变量后加上冒号将其写入来指定类型。

  1. let implicitInteger = 70
  2. let implicitDouble = 70.0
  3. let explicitDouble: Double = 70

实验

创建一个显式类型为Float且值为的常数4

值永远不会隐式转换为另一种类型。如果需要将值转换为其他类型,请显式创建所需类型的实例。

  1. let label = "The width is "
  2. let width = 94
  3. let widthLabel = label + String(width)

实验

尝试String从最后一行删除到的转换。你得到什么错误?

有一种甚至更简单的方法可以在字符串中包含值:在括号中写值,并在括号\前写反斜杠()。例如:

  1. let apples = 3
  2. let oranges = 5
  3. let appleSummary = "I have \(apples) apples."
  4. let fruitSummary = "I have \(apples + oranges) pieces of fruit."

实验

用于\()在字符串中包含浮点计算,并在问候语中包含某人的姓名。

"""对于占用多行的字符串,请使用三个双引号()。只要每个引用行的缩进都与右引号的缩进匹配,就将其删除。例如:

  1. let quotation = """
  2. I said "I have \(apples) apples."
  3. And then I said "I have \(apples + oranges) pieces of fruit."
  4. """

使用方括号([])创建数组和字典,并通过在方括号中写入索引或键来访问它们的元素。最后一个元素之后允许使用逗号。

  1. var shoppingList = ["catfish", "water", "tulips"]
  2. shoppingList[1] = "bottle of water"
  3. var occupations = [
  4. "Malcolm": "Captain",
  5. "Kaylee": "Mechanic",
  6. ]
  7. occupations["Jayne"] = "Public Relations"

数组随着添加元素而自动增长。

  1. shoppingList.append("blue paint")
  2. print(shoppingList)

要创建一个空数组或字典,请使用初始化程序语法。

  1. let emptyArray = [String]()
  2. let emptyDictionary = [String: Float]()

如果可以推断出类型信息,则可以将空数组写为[],将空字典写为,[:]例如,当您为变量设置新值或将参数传递给函数时。

  1. shoppingList = []
  2. occupations = [:]

控制流

使用ifswitch制作条件语句和使用for- inwhilerepeat-while进行循环。条件或循环变量的括号是可选的。身体周围需要支撑。

  1. let individualScores = [75, 43, 103, 87, 12]
  2. var teamScore = 0
  3. for score in individualScores {
  4. if score > 50 {
  5. teamScore += 3
  6. } else {
  7. teamScore += 1
  8. }
  9. }
  10. print(teamScore)
  11. // Prints "11"

在一条if语句中,条件必须是布尔表达式-这意味着诸如之类的代码是错误,而不是与零的隐式比较。if score { ... }

您可以将iflet一起使用,以处理可能缺少的值。这些值表示为可选值。可选值包含一个值或包含nil一个指示值缺失的值。?在值的类型后写一个问号(),以将该值标记为可选。

  1. var optionalString: String? = "Hello"
  2. print(optionalString == nil)
  3. // Prints "false"
  4. var optionalName: String? = "John Appleseed"
  5. var greeting = "Hello!"
  6. if let name = optionalName {
  7. greeting = "Hello, \(name)"
  8. }

实验

更改optionalNamenil。你得到什么问候?else如果optionalName为,添加一个设置不同问候语的子句nil

如果可选值为nil,则条件为,false并且括号中的代码将被跳过。否则,将解压缩可选值,并将其分配给之后的常量let,这将使解压缩后的值在代码块内可用。

处理可选值的另一种方法是使用??运算符提供默认值。如果缺少可选值,则使用默认值。

  1. let nickname: String? = nil
  2. let fullName: String = "John Appleseed"
  3. let informalGreeting = "Hi \(nickname ?? fullName)"

开关支持任何类型的数据和各种各样的比较操作-它们不限于整数和相等性测试。

  1. let vegetable = "red pepper"
  2. switch vegetable {
  3. case "celery":
  4. print("Add some raisins and make ants on a log.")
  5. case "cucumber", "watercress":
  6. print("That would make a good tea sandwich.")
  7. case let x where x.hasSuffix("pepper"):
  8. print("Is it a spicy \(x)?")
  9. default:
  10. print("Everything tastes good in soup.")
  11. }
  12. // Prints "Is it a spicy red pepper?"

实验

尝试删除默认情况。你得到什么错误?

注意如何let在模式中使用它来将与模式匹配的值分配给常量。

在匹配的switch case中执行代码后,程序将从switch语句退出。执行不会继续到下一个案例,因此您无需在每个案例代码的末尾显式地退出开关。

您可以使用for-in通过为每个键值对提供一对名称来遍历字典中的项目。字典是无序集合,因此它们的键和值以任意顺序进行迭代。

  1. let interestingNumbers = [
  2. "Prime": [2, 3, 5, 7, 11, 13],
  3. "Fibonacci": [1, 1, 2, 3, 5, 8],
  4. "Square": [1, 4, 9, 16, 25],
  5. ]
  6. var largest = 0
  7. for (_, numbers) in interestingNumbers {
  8. for number in numbers {
  9. if number > largest {
  10. largest = number
  11. }
  12. }
  13. }
  14. print(largest)
  15. // Prints "25"

实验

_变量名替换,并跟踪哪种数字最大。

使用while重复的代码块,直到病情变化。循环的条件可以在末尾,以确保循环至少运行一次。

  1. var n = 2
  2. while n < 100 {
  3. n *= 2
  4. }
  5. print(n)
  6. // Prints "128"
  7. var m = 2
  8. repeat {
  9. m *= 2
  10. } while m < 100
  11. print(m)
  12. // Prints "128"

您可以通过使用..<创建一系列索引来使索引保持循环。

  1. var total = 0
  2. for i in 0..<4 {
  3. total += i
  4. }
  5. print(total)
  6. // Prints "6"

使用..<做出各种省略了其上限值和使用...,使既包括值的范围。

功能和关闭

使用func声明函数。通过在函数名称后加上括号中的参数列表来调用该函数。使用->的参数名称和类型从函数的返回类型分开。

  1. func greet(person: String, day: String) -> String {
  2. return "Hello \(person), today is \(day)."
  3. }
  4. greet(person: "Bob", day: "Tuesday")

实验

删除day参数。添加一个参数以在问候语中包括今天的特色午餐。

默认情况下,函数使用其参数名称作为其参数的标签。在参数名称前写一个自定义参数标签,或写_为不使用任何参数标签。

  1. func greet(_ person: String, on day: String) -> String {
  2. return "Hello \(person), today is \(day)."
  3. }
  4. greet("John", on: "Wednesday")

使用元组生成复合值,例如,从函数返回多个值。元组的元素可以通过名称或数字来引用。

  1. func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) {
  2. var min = scores[0]
  3. var max = scores[0]
  4. var sum = 0
  5. for score in scores {
  6. if score > max {
  7. max = score
  8. } else if score < min {
  9. min = score
  10. }
  11. sum += score
  12. }
  13. return (min, max, sum)
  14. }
  15. let statistics = calculateStatistics(scores: [5, 3, 100, 3, 9])
  16. print(statistics.sum)
  17. // Prints "120"
  18. print(statistics.2)
  19. // Prints "120"

函数可以嵌套。嵌套函数可以访问在外部函数中声明的变量。您可以使用嵌套函数将代码组织成一个长的或复杂的函数。

  1. func returnFifteen() -> Int {
  2. var y = 10
  3. func add() {
  4. y += 5
  5. }
  6. add()
  7. return y
  8. }
  9. returnFifteen()

函数是一流的类型。这意味着一个函数可以返回另一个函数作为其值。

  1. func makeIncrementer() -> ((Int) -> Int) {
  2. func addOne(number: Int) -> Int {
  3. return 1 + number
  4. }
  5. return addOne
  6. }
  7. var increment = makeIncrementer()
  8. increment(7)

一个函数可以将另一个函数作为其参数之一。

  1. func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool {
  2. for item in list {
  3. if condition(item) {
  4. return true
  5. }
  6. }
  7. return false
  8. }
  9. func lessThanTen(number: Int) -> Bool {
  10. return number < 10
  11. }
  12. var numbers = [20, 19, 7, 12]
  13. hasAnyMatches(list: numbers, condition: lessThanTen)

函数实际上是闭包的一种特殊情况:可以稍后调用的代码块。闭包中的代码可以访问在创建闭包的范围内可用的变量和函数之类的东西,即使闭包在执行时位于不同的范围内,您也已经看到了嵌套函数的示例。您可以使用大括号({})将代码括起来,从而编写一个没有名称的闭包。用于in从正文中分离参数和返回类型。

  1. numbers.map({ (number: Int) -> Int in
  2. let result = 3 * number
  3. return result
  4. })

实验

重写闭包以对所有奇数返回零。

您有几种选择可以更简洁地编写闭包。当已知闭包的类型(例如委托的回调)时,可以省略其参数的类型,返回类型或两者。单条语句闭包隐式返回其唯一语句的值。

  1. let mappedNumbers = numbers.map({ number in 3 * number })
  2. print(mappedNumbers)
  3. // Prints "[60, 57, 21, 36]"

您可以通过数字而不是名称来引用参数-这种方法在非常短的闭包中特别有用。作为最后一个参数传递给函数的闭包可以在括号后立即显示。当闭包是函数的唯一参数时,可以完全省略括号。

  1. let sortedNumbers = numbers.sorted { $0 > $1 }
  2. print(sortedNumbers)
  3. // Prints "[20, 19, 12, 7]"

对象和类

使用,class后跟类的名称来创建一个类。类中的属性声明与常量或变量声明的编写方式相同,只不过它是在类的上下文中编写的。同样,方法和函数声明的编写方式相同。

  1. class Shape {
  2. var numberOfSides = 0
  3. func simpleDescription() -> String {
  4. return "A shape with \(numberOfSides) sides."
  5. }
  6. }

实验

使用添加常量属性let,并添加另一个带有参数的方法。

通过在类名称后加上括号来创建类的实例。使用点语法访问实例的属性和方法。

  1. var shape = Shape()
  2. shape.numberOfSides = 7
  3. var shapeDescription = shape.simpleDescription()

Shape该类的版本缺少重要的内容:创建实例时用于设置该类的初始化程序。使用init创建一个。

  1. class NamedShape {
  2. var numberOfSides: Int = 0
  3. var name: String
  4. init(name: String) {
  5. self.name = name
  6. }
  7. func simpleDescription() -> String {
  8. return "A shape with \(numberOfSides) sides."
  9. }
  10. }

注意如何self使用name属性来区分属性和name初始化程序的参数。创建类的实例时,初始化函数的参数像函数调用一样传递。每个属性都需要在其声明(如numberOfSides)或初始化程序(如name)中分配一个值。

deinit如果需要在释放对象之前执行一些清理,请使用来创建一个反初始化程序。

子类在其类名之后包括其超类名,并用冒号分隔。不需要类继承任何标准根类,因此您可以根据需要包含或忽略超类。

覆盖超类的实现的子类上override的方法标记为—意外覆盖方法,不带override,编译器将其检测为错误。编译器还会检测override那些实际上没有覆盖超类中任何方法的方法。

  1. class Square: NamedShape {
  2. var sideLength: Double
  3. init(sideLength: Double, name: String) {
  4. self.sideLength = sideLength
  5. super.init(name: name)
  6. numberOfSides = 4
  7. }
  8. func area() -> Double {
  9. return sideLength * sideLength
  10. }
  11. override func simpleDescription() -> String {
  12. return "A square with sides of length \(sideLength)."
  13. }
  14. }
  15. let test = Square(sideLength: 5.2, name: "my test square")
  16. test.area()
  17. test.simpleDescription()

实验

制作另一个NamedShape名为的子类,Circle它以半径和名称作为其初始值设定项的参数。在类上实现area()simpleDescription()方法Circle

除了存储的简单属性外,属性还可以具有getter和setter。

  1. class EquilateralTriangle: NamedShape {
  2. var sideLength: Double = 0.0
  3. init(sideLength: Double, name: String) {
  4. self.sideLength = sideLength
  5. super.init(name: name)
  6. numberOfSides = 3
  7. }
  8. var perimeter: Double {
  9. get {
  10. return 3.0 * sideLength
  11. }
  12. set {
  13. sideLength = newValue / 3.0
  14. }
  15. }
  16. override func simpleDescription() -> String {
  17. return "An equilateral triangle with sides of length \(sideLength)."
  18. }
  19. }
  20. var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
  21. print(triangle.perimeter)
  22. // Prints "9.3"
  23. triangle.perimeter = 9.9
  24. print(triangle.sideLength)
  25. // Prints "3.3000000000000003"

在for的setter中perimeter,新值具有隐式名称newValue。您可以在后面的括号中提供一个明确的名称set

请注意,EquilateralTriangle该类的初始化程序具有三个不同的步骤:

  1. 设置子类声明的属性的值。
  2. 调用超类的初始化程序。
  3. 更改由超类定义的属性的值。此时,也可以完成使用方法,getter或setter的任何其他设置工作。

如果您不需要计算属性,但仍然需要提供在设置新值之前和之后运行的代码,请使用willSetdidSet。只要提供的代码在初始值设定项之外更改,就可以运行您提供的代码。例如,下面的类确保其三角形的边长始终与其正方形的边长相同。

  1. class TriangleAndSquare {
  2. var triangle: EquilateralTriangle {
  3. willSet {
  4. square.sideLength = newValue.sideLength
  5. }
  6. }
  7. var square: Square {
  8. willSet {
  9. triangle.sideLength = newValue.sideLength
  10. }
  11. }
  12. init(size: Double, name: String) {
  13. square = Square(sideLength: size, name: name)
  14. triangle = EquilateralTriangle(sideLength: size, name: name)
  15. }
  16. }
  17. var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
  18. print(triangleAndSquare.square.sideLength)
  19. // Prints "10.0"
  20. print(triangleAndSquare.triangle.sideLength)
  21. // Prints "10.0"
  22. triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
  23. print(triangleAndSquare.triangle.sideLength)
  24. // Prints "50.0"

使用可选值时,可以?在方法,属性和下标之类的操作之前编写。如果之前的?值为nil,则后面的一切都会?被忽略,整个表达式的值为nil。否则,将取消包装可选值,并且所有操作之后的内容都将?作用于未包装的值。在这两种情况下,整个表达式的值都是一个可选值。

  1. let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
  2. let sideLength = optionalSquare?.sideLength

枚举和结构

使用enum创建一个枚举。像类和所有其他命名类型一样,枚举可以具有与之关联的方法。

  1. enum Rank: Int {
  2. case ace = 1
  3. case two, three, four, five, six, seven, eight, nine, ten
  4. case jack, queen, king
  5. func simpleDescription() -> String {
  6. switch self {
  7. case .ace:
  8. return "ace"
  9. case .jack:
  10. return "jack"
  11. case .queen:
  12. return "queen"
  13. case .king:
  14. return "king"
  15. default:
  16. return String(self.rawValue)
  17. }
  18. }
  19. }
  20. let ace = Rank.ace
  21. let aceRawValue = ace.rawValue

实验

编写一个通过比较两个Rank原始值来比较两个值的函数。

默认情况下,Swift分配的原始值从零开始,每次递增1,但是您可以通过显式指定值来更改此行为。在上面的示例中,Ace显式指定了的原始值1,其余原始值按顺序分配。您还可以使用字符串或浮点数作为枚举的原始类型。使用该rawValue属性访问枚举案例的原始值。

使用init?(rawValue:)初始化程序可从原始值创建枚举的实例。它返回要么枚举时对应的原始值,或者nil如果没有匹配Rank

  1. if let convertedRank = Rank(rawValue: 3) {
  2. let threeDescription = convertedRank.simpleDescription()
  3. }

枚举的大小写值是实际值,而不仅仅是写原始值的另一种方法。实际上,在没有有意义的原始价值的情况下,您不必提供原始价值。

  1. enum Suit {
  2. case spades, hearts, diamonds, clubs
  3. func simpleDescription() -> String {
  4. switch self {
  5. case .spades:
  6. return "spades"
  7. case .hearts:
  8. return "hearts"
  9. case .diamonds:
  10. return "diamonds"
  11. case .clubs:
  12. return "clubs"
  13. }
  14. }
  15. }
  16. let hearts = Suit.hearts
  17. let heartsDescription = hearts.simpleDescription()

实验

添加一个color()方法Suit,对于黑桃和球棒返回“黑色”,对于心形和菱形返回“红色”。

请注意hearts上面引用枚举大小写的两种方式:为hearts常量分配值时,枚举大小写Suit.hearts以其全名引用,因为常量没有指定显式类型。在交换机内部,枚举用缩写形式表示,.hearts因为self已经知道的值是合适的。只要知道值的类型,就可以使用缩写形式。

如果枚举具有原始值,则将这些值确定为声明的一部分,这意味着特定枚举用例的每个实例始终具有相同的原始值。枚举用例的另一种选择是具有与用例关联的值-这些值是在创建实例时确定的,并且对于枚举用例的每个实例而言,它们可以是不同的。您可以认为关联值的行为类似于枚举案例实例的存储属性。例如,考虑从服务器请求日出和日落时间的情况。服务器要么以请求的信息作为响应,要么以错误的描述作为响应。

  1. enum ServerResponse {
  2. case result(String, String)
  3. case failure(String)
  4. }
  5. let success = ServerResponse.result("6:00 am", "8:09 pm")
  6. let failure = ServerResponse.failure("Out of cheese.")
  7. switch success {
  8. case let .result(sunrise, sunset):
  9. print("Sunrise is at \(sunrise) and sunset is at \(sunset).")
  10. case let .failure(message):
  11. print("Failure... \(message)")
  12. }
  13. // Prints "Sunrise is at 6:00 am and sunset is at 8:09 pm."

实验

将第三种情况添加ServerResponse到交换机并添加到交换机。

请注意,如何将ServerResponse值从日出和日落时间中提取出来,作为将值与切换案例进行匹配的一部分。

使用struct创建的结构。结构支持许多与类相同的行为,包括方法和初始化程序。结构和类之间最重要的区别之一是,结构在代码中传递时始终会被复制,而类是通过引用传递的。

  1. struct Card {
  2. var rank: Rank
  3. var suit: Suit
  4. func simpleDescription() -> String {
  5. return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
  6. }
  7. }
  8. let threeOfSpades = Card(rank: .three, suit: .spades)
  9. let threeOfSpadesDescription = threeOfSpades.simpleDescription()

实验

编写一个函数,该函数返回一个包含一整套纸牌的数组,每个纸牌的等级和花色组合各有一张。

协议和扩展

使用protocol申报的协议。

  1. protocol ExampleProtocol {
  2. var simpleDescription: String { get }
  3. mutating func adjust()
  4. }

类,枚举和结构都可以采用协议。

  1. class SimpleClass: ExampleProtocol {
  2. var simpleDescription: String = "A very simple class."
  3. var anotherProperty: Int = 69105
  4. func adjust() {
  5. simpleDescription += " Now 100% adjusted."
  6. }
  7. }
  8. var a = SimpleClass()
  9. a.adjust()
  10. let aDescription = a.simpleDescription
  11. struct SimpleStructure: ExampleProtocol {
  12. var simpleDescription: String = "A simple structure"
  13. mutating func adjust() {
  14. simpleDescription += " (adjusted)"
  15. }
  16. }
  17. var b = SimpleStructure()
  18. b.adjust()
  19. let bDescription = b.simpleDescription

实验

向添加新的要求ExampleProtocol。你需要什么样的变化做出SimpleClassSimpleStructure使他们仍然符合协议?

请注意,mutating在的声明中使用了关键字,SimpleStructure以标记用于修改结构的方法。的声明SimpleClass不需要其任何标记为变异的方法,因为类上的方法始终可以修改该类。

用于extension向现有类型添加功能,例如新方法和计算属性。您可以使用扩展将协议一致性添加到在其他地方声明的类型,甚至添加到从库或框架导入的类型。

  1. extension Int: ExampleProtocol {
  2. var simpleDescription: String {
  3. return "The number \(self)"
  4. }
  5. mutating func adjust() {
  6. self += 42
  7. }
  8. }
  9. print(7.simpleDescription)
  10. // Prints "The number 7"

实验

Double添加absoluteValue属性的类型编写扩展名。

您可以像使用任何其他命名类型一样使用协议名称,例如,创建具有不同类型但都符合一个协议的对象的集合。当您使用类型为协议类型的值时,协议定义之外的方法将不可用。

  1. let protocolValue: ExampleProtocol = a
  2. print(protocolValue.simpleDescription)
  3. // Prints "A very simple class. Now 100% adjusted."
  4. // print(protocolValue.anotherProperty) // Uncomment to see the error

即使变量protocolValue的运行时类型为SimpleClass,编译器也将其视为的给定类型ExampleProtocol。这意味着除了协议一致性之外,您不能意外访问该类实现的方法或属性。

错误处理

您可以使用采用该Error协议的任何类型来表示错误。

  1. enum PrinterError: Error {
  2. case outOfPaper
  3. case noToner
  4. case onFire
  5. }

使用throw抛出一个错误,并throws标记,可以抛出一个错误的功能。如果在函数中引发错误,该函数将立即返回,并且调用该函数的代码将处理该错误。

  1. func send(job: Int, toPrinter printerName: String) throws -> String {
  2. if printerName == "Never Has Toner" {
  3. throw PrinterError.noToner
  4. }
  5. return "Job sent"
  6. }

有几种处理错误的方法。一种方法是使用do- catch。在该do块内部,您可以标记可能会通过try在其前面写入而引发错误的代码。在catch块内,将自动为错误指定名称,error除非您为其指定其他名称。

  1. do {
  2. let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")
  3. print(printerResponse)
  4. } catch {
  5. print(error)
  6. }
  7. // Prints "Job sent"

实验

将打印机名称更改为,以便该函数引发错误。"Never Has Toner"send(job:toPrinter:)

您可以提供多个catch处理特定错误的块。catch就像case在切换之后一样,您可以编写一个模式。

  1. do {
  2. let printerResponse = try send(job: 1440, toPrinter: "Gutenberg")
  3. print(printerResponse)
  4. } catch PrinterError.onFire {
  5. print("I'll just put this over here, with the rest of the fire.")
  6. } catch let printerError as PrinterError {
  7. print("Printer error: \(printerError).")
  8. } catch {
  9. print(error)
  10. }
  11. // Prints "Job sent"

实验

添加代码以在do块内引发错误。您需要引发哪种错误,以便由第一个catch块处理该错误?那第二块和第三块呢?

处理错误的另一种方法是try?用于将结果转换为可选的。如果函数抛出错误,则将丢弃特定的错误,结果为nil。否则,结果是一个可选值,其中包含函数返回的值。

  1. let printerSuccess = try? send(job: 1884, toPrinter: "Mergenthaler")
  2. let printerFailure = try? send(job: 1885, toPrinter: "Never Has Toner")

defer写的是在功能的所有其它代码后执行代码块,只是在函数返回之前。无论函数是否引发错误,都将执行代码。defer即使需要在不同的时间执行设置和清除代码,您也可以使用它们彼此相邻。

  1. var fridgeIsOpen = false
  2. let fridgeContent = ["milk", "eggs", "leftovers"]
  3. func fridgeContains(_ food: String) -> Bool {
  4. fridgeIsOpen = true
  5. defer {
  6. fridgeIsOpen = false
  7. }
  8. let result = fridgeContent.contains(food)
  9. return result
  10. }
  11. fridgeContains("banana")
  12. print(fridgeIsOpen)
  13. // Prints "false"

泛型

在尖括号内写一个名称,以构成通用函数或类型。

  1. func makeArray<Item>(repeating item: Item, numberOfTimes: Int) -> [Item] {
  2. var result = [Item]()
  3. for _ in 0..<numberOfTimes {
  4. result.append(item)
  5. }
  6. return result
  7. }
  8. makeArray(repeating: "knock", numberOfTimes: 4)

您可以使函数和方法以及类,枚举和结构形成通用形式。

  1. // Reimplement the Swift standard library's optional type
  2. enum OptionalValue<Wrapped> {
  3. case none
  4. case some(Wrapped)
  5. }
  6. var possibleInteger: OptionalValue<Int> = .none
  7. possibleInteger = .some(100)

where在正文之前使用权利来指定需求列表,例如,要求类型实现协议,要求两个类型相同或要求一个类具有特定的超类。

  1. func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> Bool
  2. where T.Element: Equatable, T.Element == U.Element
  3. {
  4. for lhsItem in lhs {
  5. for rhsItem in rhs {
  6. if lhsItem == rhsItem {
  7. return true
  8. }
  9. }
  10. }
  11. return false
  12. }
  13. anyCommonElements([1, 2, 3], [3])

实验

修改该anyCommonElements(_:_:)函数以使该函数返回任何两个序列共同具有的元素的数组。

写作和写作一样。<T: Equatable><T> ... where T: Equatable