IOS-swift5.1快速入門之旅

  • 2019 年 10 月 10 日
  • 筆記

快速之旅

傳統表明,新語言中的第一個程序應在屏幕上打印「Hello,world!」字樣。在Swift中,這可以在一行中完成:

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

如果你用C或Objective-C編寫代碼,這個語法看起來很熟悉 – 在Swift中,這行代碼是一個完整的程序。您無需為輸入/輸出或字符串處理等功能導入單獨的庫。在全局範圍編寫的代碼用作程序的入口點,因此您不需要main()函數。您也不需要在每個語句的末尾寫分號。

本導覽通過向您展示如何完成各種編程任務,為您提供了足夠的信息來開始在Swift中編寫代碼。如果您不理解某些內容,請不要擔心 – 本書其餘部分將詳細介紹本導覽中介紹的所有內容。

注意

為了獲得最佳體驗,請將本章打開為Xcode中的遊樂場。Playgrounds允許您編輯代碼清單並立即查看結果。

下載遊樂場

簡單的價值觀

使用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製作條件語句和使用forinwhilerepeatwhile進行循環。條件或循環變量周圍的括號是可選的。需要身體周圍的牙箍。

  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語句。執行不會繼續下一種情況,因此不需要在每個案例代碼的末尾明確地中斷交換機。

您可以使用forin通過提供一對用於每個鍵值對的名稱來迭代字典中的項目。字典是無序集合,因此它們的鍵和值以任意順序迭代。

  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 (kind, 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,它將radius和name作為其初始值設定項的參數。在類上實現一個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"

在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. }

有幾種方法可以處理錯誤。一種方法是使用docatch。在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

版本兼容性

基礎

BETA軟件

本文檔包含有關正在開發的API或技術的初步信息。此信息可能會發生變化,根據本文檔實施的軟件應使用最終操作系統軟件進行測試。

了解有關使用Apple測試版軟件的更多信息