10000 字|Swift語法全面解析

萬字長文,每個知識點都對應了demo,理論與實踐結合,講解地很是到位。git

Swift介紹

Swift 是一門開發 iOS, macOS, watchOS 和 tvOS 應用的新語言。
swift 是一種安全,快速和互動的編程語言。
swift 支持代碼預覽(playgrounds),這個特性能夠容許程序員在不編譯和運行應用程序的前提下運行 Swift 代碼並實時查看結果。程序員

Swift 經過採用現代編程模式來避免大量常見編程錯誤:express

  • 變量始終在使用前初始化。編程

  • 檢查數組索引超出範圍的錯誤。小程序

  • 檢查整數是否溢出。swift

  • 可選值確保明確處理 nil 值。設計模式

  • 內存被自動管理。api

  • 錯誤處理容許從意外故障控制恢復。數組

基礎部分

常量和變量

聲明常量和變量, 常量和變量必須在使用前聲明,使用 let 來聲明常量,使用 var 來聲明變量。
示例:安全

let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0

// 類型註解
var welcomeMessage: String

註釋

單行註釋雙正斜槓(//), 多行註釋(/* 多行的 */)。Swift 的多行註釋能夠嵌套在其它的多行註釋之中。
示例:

// 這是一個註釋

/* 這也是一個註釋,
可是是多行的 */

/* 這是第一個多行註釋的開頭
/* 這是第二個被嵌套的多行註釋 */
這是第一個多行註釋的結尾 */

分號

Swift 並不強制要求你在每條語句的結尾處使用分號(;)。
同一行內寫多條獨立的語句必須用分號分隔。

let cat = "🐱"print(cat)
// 輸出「🐱」

整數、浮點數

統一使用 Int 能夠提升代碼的可複用性,避免不一樣類型數字之間的轉換, 而且匹配數字的類型推斷。
示例:

let minValue = UInt8.min  // minValue 爲 0,是 UInt8 類型
let maxValue = UInt8.max  // maxValue 爲 255,是 UInt8 類型

類型安全和類型推斷

Swift 是一門類型安全的語言,這意味着 Swift 可讓你清楚地知道值的類型。
若是你沒有顯式指定類型,Swift 會使用類型推斷來選擇合適的類型。(int、double)。
示例:

let meaningOfLife = 42
// meaningOfLife 會被推測爲 Int 類型

let pi = 3.14159
// pi 會被推測爲 Double 類型

數值型字面量、數值型類型轉換

示例:

let decimalInteger = 17
let binaryInteger = 0b10001       // 二進制的17
let octalInteger = 0o21           // 八進制的17
let hexadecimalInteger = 0x11     // 十六進制的17

類型別名

類型別名(type aliases)就是給現有類型定義另外一個名字。你可使用 typealias 關鍵字來定義類型別名。
示例:

typealias AudioSample = UInt16
var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound 如今是 0

布爾值

示例:

let orangesAreOrange = true
let turnipsAreDelicious = false

元組

元組(tuples)把多個值組合成一個複合值。元組內的值能夠是任意類型,並不要求是相同類型。
示例:

let http404Error = (404, "Not Found")
// http404Error 的類型是 (Int, String),值是 (404, "Not Found")

可選類型

使用可選類型(optionals)來處理值可能缺失的狀況。可選類型表示兩種可能:或者有值, 你能夠解析可選類型訪問這個值, 或者根本沒有值。
示例:

var serverResponseCode: Int? = 404
// serverResponseCode 包含一個可選的 Int 值 404
serverResponseCode = nil
// serverResponseCode 如今不包含值

錯誤處理

錯誤處理,應對程序執行中可能會遇到的錯誤條件。
示例:

func makeASandwich() throws {
    // ...
}

do {
    try makeASandwich()
    eatASandwich()
} catch SandwichError.outOfCleanDishes {
    washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
    buyGroceries(ingredients)
}

斷言和先決條件

斷言和先決條件,是在運行時所作的檢查。

let age = -3
assert(age >= 0, "A person's age cannot be less than zero")
// 由於 age < 0,因此斷言會觸發

基本運算符

Swift 支持大部分標準 C 語言的運算符,還提供了 C 語言沒有的區間運算符,例如 a..<b 或 a...b。

賦值運算符,算術運算符,組合賦值運算符,比較運算符,三元運算符,空合運算符,區間運算符,邏輯運算符

運算符分爲一元、二元和三元運算符。
閉區間運算符(a...b)定義一個包含從 a 到 b(包括 a 和 b)的全部值的區間。
半開區間運算符(a..<b)定義一個從 a 到 b 但不包括 b 的區間。
閉區間操做符有另外一個表達形式,能夠表達往一側無限延伸的區間,(a...,...b)。
示例:

let names = ["Anna""Alex""Brian""Jack"]
let count = names.count
for i in 0..<count {
    print("第 \(i + 1) 我的叫 \(names[i])")
}
// 第 1 我的叫 Anna
// 第 2 我的叫 Alex
// 第 3 我的叫 Brian
// 第 4 我的叫 Jack

字符串和字符

字符串字面量,字符串插值,計算字符數量,訪問和修改字符串,子字符串,比較字符串

初始化空字符串,字符串可變性,字符串是值類型,鏈接字符串和字符(+,+=)。
使用字符,可經過 for-in 循環來遍歷字符串,獲取字符串中每個字符的值。
字符串插值是一種構建新字符串的方式,能夠在其中包含常量、變量、字面量和表達式。能夠在已有字符串中插入常量、變量、字面量和表達式從而造成更長的字符串。
Swift 提供了三種方式來比較文本值:字符串字符相等、前綴相等和後綴相等。
示例:

// 多行字符串字面量
let quotation = """ The White Rabbit put on his spectacles.  "Where shall I begin,
please your Majesty?" he asked. "Begin at the beginning," the King said gravely, "and go on
till you come to the end; then stop." """

// 下面兩個字符串實際上是同樣的
let singleLineString = "These are the same."
let multilineString = """ These are the same. """

// 字符串插值
let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
// message 是 "3 times 2.5 is 7.5"

// 計算字符數量
var word = "cafe"
print("the number of characters in \(word) is \(word.count)")
// 打印輸出「the number of characters in cafe is 4」

var emptyString = ""               // 空字符串字面量
var anotherEmptyString = String()  // 初始化方法
// 兩個字符串均爲空並等價。

let catCharacters: [Character] = ["C""a""t""!"]
let catString = String(catCharacters)
print(catString)
// 打印輸出:「Cat!」

集合類型

Swift 語言提供數組(Array)、集合(Set)和字典(Dictionary)三種基本的集合類型用來存儲集合數據。數組是有序數據的集。集合是無序無重複數據的集。字典是無序的鍵值對的集。

集合的可變性,數組(Arrays),集合(Sets),集合操做,字典

數組使用有序列表存儲同一類型的多個值。相同的值能夠屢次出如今一個數組的不一樣位置中。
集合用來存儲相同類型而且沒有肯定順序的值。當集合元素順序不重要時或者但願確保每一個元素只出現一次時可使用集合而不是數組。
集合操做,能夠高效地完成集合的一些基本操做,好比把兩個集合組合到一塊兒,判斷兩個集合共有元素,或者判斷兩個集合是否全包含,部分包含或者不相交。
字典是一種無序的集合,它存儲的是鍵值對之間的關係,其全部鍵的值須要是相同的類型,全部值的類型也須要相同。每一個值(value)都關聯惟一的鍵(key),鍵做爲字典中這個值數據的標識符。
示例:

// 集合
var someInts = [Int]()
print("someInts is of type [Int] with \(someInts.count) items.")
// 打印「someInts is of type [Int] with 0 items.」

var threeDoubles = Array(repeating: 0.0, count: 3)
// threeDoubles 是一種 [Double] 數組,等價於 [0.0, 0.0, 0.0]

var anotherThreeDoubles = Array(repeating: 2.5, count: 3)
// anotherThreeDoubles 被推斷爲 [Double],等價於 [2.5, 2.5, 2.5]
var sixDoubles = threeDoubles + anotherThreeDoubles
// sixDoubles 被推斷爲 [Double],等價於 [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]

// enumerated() 方法遍歷數組
var shoppingList: [String] = ["Eggs""Milk"]
for (index, value) in shoppingList.enumerated() {
    print("Item \(String(index + 1)): \(value)")
}

控制流

For-In 循環,While 循環(Repeat-While),條件語句,控制轉移語句,提早退出(guard),檢測 API 可用性

像 if 語句同樣,guard 的執行取決於一個表達式的布爾值。咱們可使用 guard 語句來要求條件必須爲真時,以執行 guard 語句後的代碼。不一樣於 if 語句,一個 guard 語句老是有一個 else 從句,若是條件不爲真則執行 else 從句中的代碼。
Swift 內置支持檢查 API 可用性,編譯器使用 SDK 中的可用信息來驗證咱們的代碼中使用的全部 API 在項目指定的部署目標上是否可用。若是咱們嘗試使用一個不可用的 API,Swift 會在編譯時報錯。
示例:

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

let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
for (animalName, legCount) in numberOfLegs {
    print("\(animalName)s have \(legCount) legs")
}

//  repeat-while 循環的通常格式
repeat {
    statements
} while condition


// 提早退出
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.」

函數

函數的定義與調用,函數參數與返回值,函數參數標籤和參數名稱,函數類型,嵌套函數

可選元組返回類型。
定義一個輸入輸出參數時,在參數定義前加 inout 關鍵字。
示例:

// 函數
func greet(person: String) -> String {
    let greeting = "Hello, " + person + "!"
    return greeting
}

func greet(person: String, from hometown: String) -> String {
    return "Hello \(person)!  Glad you could visit from \(hometown)."
}
print(greet(person: "Bill", from: "Cupertino"))
// 打印「Hello Bill!  Glad you could visit from Cupertino.」

// 可選元組返回類型
func minMax(array: [Int]) -> (min: Int, max: Int)? {
    if array.isEmpty { return nil }
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1..<array.count] {
        if value < currentMin {
            currentMin = value
        } else if value > currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}

// 隱式返回的函數
func greeting(for person: String) -> String {
    "Hello, " + person + "!"
}
print(greeting(for"Dave"))
// 打印 "Hello, Dave! // 參數標籤 func greet(person: String, from hometown: String) -> String {     return "Hello \(person)!  Glad you could visit from \(hometown)." } print(greet(person: "Bill", from: "Cupertino")) // 打印「Hello Bill!  Glad you could visit from Cupertino.」 

閉包

閉包是自包含的函數代碼塊,能夠在代碼中被傳遞和使用。與一些編程語言中的匿名函數(Lambdas)比較類似。

閉包表達式,尾隨閉包,值捕獲,閉包是引用類型,逃逸閉包(@escaping),自動閉包

若是你須要將一個很長的閉包表達式做爲最後一個參數傳遞給函數,將這個閉包替換成爲尾隨閉包的形式頗有用。
閉包能夠在其被定義的上下文中捕獲常量或變量。即便定義這些常量和變量的原做用域已經不存在,閉包仍然能夠在閉包函數體內引用和修改這些值。
示例:

// 閉包表達式語法
{ (parameters) -> return type in
    statements
}

// 尾隨閉包
let digitNames = [
    0: "Zero", 1: "One", 2: "Two",   3: "Three", 4: "Four",
    5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let numbers = [16, 58, 510]
let strings = numbers.map {
    (number) -> String in
    var number = number
    var output = ""
    repeat {
        output = digitNames[number % 10]! + output
        number /= 10
    } while number > 0
    return output
}
// strings 常量被推斷爲字符串類型數組,即 [String]
// 其值爲 ["OneSix""FiveEight""FiveOneZero"]

// 值捕獲
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementer() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementer
}

// 自動閉包,延遲求值
var customersInLine = ["Chris""Alex""Ewa""Barry""Daniella"]
print(customersInLine.count)
// 打印出「5」

let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)
// 打印出「5」

print("Now serving \(customerProvider())!")
// Prints "Now serving Chris!"
print(customersInLine.count)
// 打印出「4」

枚舉

使用 enum 關鍵詞來建立枚舉而且把它們的整個定義放在一對大括號內。

枚舉語法,使用 Switch 語句匹配枚舉值,枚舉成員的遍歷,關聯值,原始值(默認值),遞歸枚舉(indirect)

能夠定義 Swift 枚舉來存儲任意類型的關聯值,每一個枚舉成員的關聯值類型能夠各不相同。
示例:

// 枚舉語法
enum SomeEnumeration {
    // 枚舉定義放在這裏
}

enum CompassPoint {
    case north
    case south
    case east
    case west
}

enum Planet {
    case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}

let somePlanet = Planet.earth
switch somePlanet {
case .earth:
    print("Mostly harmless")
default:
    print("Not a safe place for humans")
}
// 打印「Mostly harmless」

// 關聯值
enum Barcode {
    case upc(Int, Int, Int, Int)
    case qrCode(String)
}
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")

switch productBarcode {
case let .upc(numberSystem, manufacturer, product, check):
    print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case let .qrCode(productCode):
    print("QR code: \(productCode).")
}
// 打印「QR code: ABCDEFGHIJKLMNOP.」

// 遞歸枚舉
indirect enum ArithmeticExpression {
    case number(Int)
    case addition(ArithmeticExpression, ArithmeticExpression)
    case multiplication(ArithmeticExpression, ArithmeticExpression)
}
let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))
// (5 + 4) * 2

func evaluate(_ expression: ArithmeticExpression) -> Int {
    switch expression {
    case let .number(value):
        return value
    case let .addition(left, right):
        return evaluate(left) + evaluate(right)
    case let .multiplication(left, right):
        return evaluate(left) * evaluate(right)
    }
}

print(evaluate(product))
// 打印「18」

結構體和類

結構體和類對比,結構體和枚舉是值類型,類是引用類型

結構體和類做爲一種通用而又靈活的結構,成爲了人們構建代碼的基礎。你可使用定義常量、變量和函數的語法,爲你的結構體和類定義屬性、添加方法。
示例:

// 類和結構體
struct SomeStructure {
    // 在這裏定義結構體
}
class SomeClass {
    // 在這裏定義類
}

struct Resolution {
    var width = 0
    var height = 0
}
class VideoMode {
    var resolution = Resolution()
    var interlaced = false
    var frameRate = 0.0
    var name: String?
}

屬性

存儲屬性,計算屬性,屬性觀察器,屬性包裝器,全局變量和局部變量,類型屬性(static)

屬性將值與特定的類、結構體或枚舉關聯。存儲屬性會將常量和變量存儲爲實例的一部分,而計算屬性則是直接計算(而不是存儲)值。計算屬性能夠用於類、結構體和枚舉,而存儲屬性只能用於類和結構體。
屬性觀察器監控和響應屬性值的變化,每次屬性被設置值的時候都會調用屬性觀察器,即便新值和當前值相同的時候也不例外。

  • willSet 在新的值被設置以前調用

  • didSet 在新的值被設置以後調用

屬性包裝器在管理屬性如何存儲和定義屬性的代碼之間添加了一個分隔層。
類型屬性也是經過點運算符來訪問。可是,類型屬性是經過類型自己來訪問,而不是經過實例。
示例:

// 屬性
struct Point {
    var x = 0.0, y = 0.0
}
struct Size {
    var width = 0.0, height = 0.0
}
struct Rect {
    var origin = Point()
    var size = Size()       //存儲屬性
    var center: Point {     //計算型屬性
        get {
            let centerX = origin.x + (size.width / 2)
            let centerY = origin.y + (size.height / 2)
            return Point(x: centerX, y: centerY)
        }
        set(newCenter) {
            origin.x = newCenter.x - (size.width / 2)
            origin.y = newCenter.y - (size.height / 2)
        }
    }
}
var square = Rect(origin: Point(x: 0.0, y: 0.0),
    size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center
square.center = Point(x: 15.0, y: 15.0)
print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
// 打印「square.origin is now at (10.0, 10.0)」

// 屬性包裝器
@propertyWrapper
struct TwelveOrLess {
    private var number = 0
    var wrappedValue: Int {
        get { return number }
        set { number = min(newValue, 12) }
    }
}

方法

實例方法(Instance Methods),類型方法(static)

方法是與某些特定類型相關聯的函數。
類、結構體、枚舉均可以定義實例方法;實例方法爲給定類型的實例封裝了具體的任務與功能。
類、結構體、枚舉也能夠定義類型方法;類型方法與類型自己相關聯。
示例:

// 方法
class Counter {
    var count = 0
    func increment() {
        count += 1
    }
    func increment(by amount: Int) {
        count += amount
    }
    func reset() {
        count = 0
    }
}

下標

下標能夠定義在類、結構體和枚舉中,是訪問集合、列表或序列中元素的快捷方式

下標語法(subscript),下標用法,下標選項,類型下標(static)

subscript(index: Int) -> Int {
    get {
      // 返回一個適當的 Int 類型的值
    }
    set(newValue) {
      // 執行適當的賦值操做
    }
}

// 示例
struct TimesTable {
    let multiplier: Int
    subscript(index: Int) -> Int {
        return multiplier * index
    }
}
let threeTimesTable = TimesTable(multiplier: 3)
print("six times three is \(threeTimesTable[6])")
// 打印「six times three is 18」

var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
numberOfLegs["bird"] = 2

// 類型下標
enum Planet: Int {
    case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
    static subscript(n: Int) -> Planet {
        return Planet(rawValue: n)!
    }
}
let mars = Planet[4]
print(mars)

繼承

定義一個基類,子類生成,重寫(override),防止重寫(final)

不繼承於其它類的類,稱之爲基類。
示例:

// 繼承
class SomeClass: SomeSuperclass {
    // 這裏是子類的定義
}

class Vehicle {
    var currentSpeed = 0.0
    var description: String {
        return "traveling at \(currentSpeed) miles per hour"
    }
    func makeNoise() {
        // 什麼也不作——由於車輛不必定會有噪音
    }
}

class Car: Vehicle {
    var gear = 1
    override var description: String {
        return super.description + " in gear \(gear)"
    }
}

class AutomaticCar: Car {
    override var currentSpeed: Double {
        didSet {
            gear = Int(currentSpeed / 10.0) + 1
        }
    }
}

構造過程

構造過程是使用類、結構體或枚舉類型的實例以前的準備過程。

存儲屬性的初始賦值,自定義構造過程,默認構造器,值類型的構造器代理,類的繼承和構造過程,可失敗構造器,必要構造器(required)

構造器能夠經過調用其它構造器來完成實例的部分構造過程。這一過程稱爲構造器代理,它能避免多個構造器間的代碼重複。
Swift 爲類類型提供了兩種構造器來確保實例中全部存儲型屬性都能得到初始值,它們被稱爲指定構造器和便利構造器。
能夠在一個類,結構體或是枚舉類型的定義中,添加一個或多個可失敗構造器。其語法爲在 init 關鍵字後面添加問號(init?)。
必要構造器,在類的構造器前添加 required 修飾符代表全部該類的子類都必須實現該構造器。
示例:

// 構造過程
init() {
    // 在此處執行構造過程
}

struct Fahrenheit {
    var temperature: Double
    init() {
        temperature = 32.0
    }
}
var f = Fahrenheit()
print("The default temperature is \(f.temperature)° Fahrenheit")
// 打印「The default temperature is 32.0° Fahrenheit」

struct Color {
    let red, green, blue: Double
    init(red: Double, green: Double, blue: Double) {
        self.red   = red
        self.green = green
        self.blue  = blue
    }
    init(white: Double) {
        red   = white
        green = white
        blue  = white
    }
}

析構過程

析構器只適用於類類型,當一個類的實例被釋放以前,析構器會被當即調用。析構器用關鍵字 deinit 來標示,相似於構造器要用 init 來標示。
Swift 會自動釋放再也不須要的實例以釋放資源。
示例:

// 析構過程
deinit {
    // 執行析構過程
}

class Bank {
    static var coinsInBank = 10_000
    static func distribute(coins numberOfCoinsRequested: Int) -> Int {
        let numberOfCoinsToVend = min(numberOfCoinsRequested, coinsInBank)
        coinsInBank -= numberOfCoinsToVend
        return numberOfCoinsToVend
    }
    static func receive(coins: Int) {
        coinsInBank += coins
    }
}

class Player {
    var coinsInPurse: Int
    init(coins: Int) {
        coinsInPurse = Bank.distribute(coins: coins)
    }
    func win(coins: Int) {
        coinsInPurse += Bank.distribute(coins: coins)
    }
    deinit {
        Bank.receive(coins: coinsInPurse)
    }
}

可選鏈式調用

可選鏈式調用是一種能夠在當前值可能爲 nil 的可選值上請求和調用屬性、方法及下標的方法。
經過在想調用的屬性、方法,或下標的可選值後面放一個問號(?),能夠定義一個可選鏈。相似在可選值後面放一個歎號(!)來強制展開它的值。它們的主要區別在於當可選值爲空時可選鏈式調用只會調用失敗,然而強制展開將會觸發運行時錯誤。
示例:

class Person {
    var residence: Residence?
}

class Residence {
    var numberOfRooms = 1
}
let john = Person()
let roomCount = john.residence!.numberOfRooms
// 這會引起運行時錯誤

if let roomCount = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}
// 打印「Unable to retrieve the number of rooms.」

john.residence = Residence()

if let roomCount = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}
// 打印「John's residence has 1 room(s).」 

錯誤處理

錯誤處理(Error handling) 是響應錯誤以及從錯誤中恢復的過程。Swift 在運行時提供了拋出、捕獲、傳遞和操做可恢復錯誤(recoverable errors)的一等支持。

表示與拋出錯誤,處理錯誤,指定清理操做

在 Swift 中,錯誤用遵循 Error 協議的類型的值來表示。
Swift 中有 4 種處理錯誤的方式。能夠把函數拋出的錯誤傳遞給調用此函數的代碼(throws)、用 do-catch 語句處理錯誤、將錯誤做爲可選類型處理(try?)、或者斷言此錯誤根本不會發生(try!)。
defer 語句將代碼的執行延遲到當前的做用域退出以前。
示例:

// 錯誤處理
enum VendingMachineError: Error {
    case invalidSelection                     //選擇無效
    case insufficientFunds(coinsNeeded: Int) //金額不足
    case outOfStock                             //缺貨
}
throw VendingMachineError.insufficientFunds(coinsNeeded: 5)

var vendingMachine = VendingMachine()
vendingMachine.coinsDeposited = 8
do {
    try buyFavoriteSnack(person: "Alice", vendingMachine: vendingMachine)
    print("Success! Yum.")
} catch VendingMachineError.invalidSelection {
    print("Invalid Selection.")
} catch VendingMachineError.outOfStock {
    print("Out of Stock.")
} catch VendingMachineError.insufficientFunds(let coinsNeeded) {
    print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.")
} catch {
    print("Unexpected error: \(error).")
}
// 打印「Insufficient funds. Please insert an additional 2 coins.」

// 指定清理操做
func processFile(filename: String) throws {
    if exists(filename) {
        let file = open(filename)
        defer {
            close(file)
        }
        while let line = try file.readline() {
            // 處理文件。
        }
        // close(file) 會在這裏被調用,即做用域的最後。
    }
}

類型轉換

類型轉換在 Swift 中使用 is 和 as 操做符實現。這兩個操做符分別提供了一種簡單達意的方式去檢查值的類型或者轉換它的類型。

爲類型轉換定義類層次,檢查類型(is),向下轉型(as? 或 as!),Any 和 AnyObject 的類型轉換

能夠將類型轉換用在類和子類的層次結構上,檢查特定類實例的類型而且轉換這個類實例的類型成爲這個層次結構中的其餘類型。
Swift 爲不肯定類型提供了兩種特殊的類型別名:

  • Any 能夠表示任何類型,包括函數類型。

  • AnyObject 能夠表示任何類類型的實例。

示例:

// 類型轉換
// 一個基類 MediaItem
class MediaItem {
 var name: String
 init(name: String) {
  self.name = name
 }
}

class Movie: MediaItem {
 var director: String
 init(name: String, director: String) {
  self.director = director
  super.init(name: name)
 }
}

class Song: MediaItem {
 var artist: String
 init(name: String, artist: String) {
  self.srtist = artist
  super.init(name: name)
 }
}

let library = [
 Movie(name: "Casablanca", director: "Micheal Curtiz"),
 Song(name: "Blue Suede Shose", artist: "Elvis Presley"),
 Movie(name: "Citizen Kane", director: "Orson Wells"),
 Song(name: "The One And Only", artist: "Chesney Hawkes"),
 Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
]
var movieCount = 0
var songCount = 0

for item in library {
 if item is Movie {
  movieCount += 1
 } else if item is Song {
  songCount += 1
 }
}

print("Media library contains \(movieCount) movies and \(songCount)")
// 打印「Media library contains 2 movies and 3 songs」

for item in library {
 if let movie = item as? Movie {
  print("Movie: \(movie.name), dir. \(movie.director)")
 } else if let song = item as? Song {
  print("Song: \(song.name), by \(song.artist)")
 }
}
// Movie: Casablanca, dir. Michael Curtiz
// Song: Blue Suede Shoes, by Elvis Presley
// Movie: Citizen Kane, dir. Orson Welles
// Song: The One And Only, by Chesney Hawkes
// Song: Never Gonna Give You Up, by Rick Astley

嵌套類型

Swift 容許定義嵌套類型,能夠在支持的類型中定義嵌套的枚舉、類和結構體。

嵌套類型實踐,引用嵌套類型

要在一個類型中嵌套另外一個類型,將嵌套類型的定義寫在其外部類型的 {} 內,並且能夠根據須要定義多級嵌套。
示例:

// 嵌套類型
stuct BlackjackCard {
 // 嵌套的 Suit 枚舉
 enum Suit: Character {
  case spades = "1", hearts = "2", diamonds = "3", clubs = "4"
 }
 
 // 嵌套的 Rank 枚舉
 enum Rank: Int {
  case two = 2, three, four, five, six, seven, eight, nine, ten
  case jack, queen, king, ace
  struct Values {
   let first: Int, second: Int?
  }
  var values: Values {
   switch self {
   case .ace:
    return Values(first: 1, second: 11)
   case .jack, .queen, .king:
    return Values(first: 10, second: nil)
   default:
    return Values(first: self.rawValue, second: nil)
   }
  }
 }
 
 // BlackjackCard 的屬性和方法
 let rank: Rank, suit: Suit
 var description: String {
  var output = "suit is \(suit.rawValue),"
  output += " value is \(rank.values.first)"
  if let second = rank.values.second {
   output += " or \(second)"
  }
  return output
 }
}

let theAceOfSpades = BlackjackCard(rank: .ace, suit: .spades)
print("theAceOfSpades: \(theAceOfSpades.description)")
// 打印「theAceOfSpades: suit is 1, value is 1 or 11」

let heartsSymbol = BlackjackCard.Suit.hearts.rawValue
// 2

擴展

擴展能夠給一個現有的類,結構體,枚舉,還有協議添加新的功能。

擴展的語法,計算型屬性,構造器,方法,下標,嵌套類型

Swift 中的擴展能夠:

  • 添加計算型實例屬性和計算型類屬性

  • 定義實例方法和類方法

  • 提供新的構造器

  • 定義下標

  • 定義和使用新的嵌套類型

  • 使已經存在的類型遵循(conform)一個協議

擴展語法:

extension SomeType {
  // 在這裏給 SomeType 添加新的功能
}

擴展能夠給現有類型添加計算型實例屬性和計算型類屬性。
擴展能夠給現有的類型添加新的構造器。
擴展能夠給現有類型添加新的實例方法和類方法。
擴展能夠給現有的類型添加新的下標。
擴展能夠給現有的類,結構體,還有枚舉添加新的嵌套類型。
示例:

// 擴展的語法
extension SomeType {
  // 在這裏給 SomeType 添加新的功能
}
// 添加一個或多個協議
extension SomeType: SomeProtocol, AnotherProtocol {
  // 協議所須要的實現寫在這裏
}

struct Size {
 var width = 0.0, height = 0.0
}
struct Point {
 var x = 0.0, y = 0.0
}
struct Rect {
 var origin = Point()
 var size = Size()
}

extension Rect {
 init(center: Point, size: Size) {
  let originX = center.x - (size.width / 2)
  let originY = center.y - (size.height / 3)
  self.init(origin: Point(x: originX, y: originY), size: size)
 }
}
let centerRect = Rect(center: Point(x: 4.0, y: 4.0), 
 size: Size(width: 3.0, height: 3.0))
// centerRect 的 origin 是 (2.5, 2.5) 而且它的 size 是 (3.0, 3.0)

extension Int {
 func repetitions(task: () -> Void) {
  for _ in 0..<self {
   task()
  }
 }
}
3.repetitions {
 print("Hello!")
}
// Hello!
// Hello!
// Hello!

extension Int {
 mutating func square() {
  self = self * self
 }
}
var somtInt = 3
someInt.square()
// someInt 如今是9

協議

協議定義了一個藍圖,規定了用來實現某一特定任務或者功能的方法、屬性,以及其餘須要的東西。
類、結構體或枚舉均可以遵循協議,併爲協議定義的這些要求提供具體實現。

協議語法,屬性要求,方法要求,異變方法要求,構造器要求,協議做爲類型,委託,協議類型的集合,協議的繼承,類專屬的協議,協議合成,檢查協議一致性,可選的協議要求,協議擴展,

協議語法

protocol SomeProtocol {
    // 這裏是協議的定義部分
}

協議能夠要求遵循協議的類型提供特定名稱和類型的實例屬性或類型屬性。
協議能夠要求遵循協議的類型實現某些指定的實例方法或類方法。
在值類型(即結構體和枚舉)的實例方法中,將 mutating 關鍵字做爲方法的前綴,寫在 func 關鍵字以前,表示能夠在該方法中修改它所屬的實例以及實例的任意屬性的值。
協議能夠要求遵循協議的類型實現指定的構造器。
委託是一種設計模式,它容許類或結構體將一些須要它們負責的功能委託給其餘類型的實例。
示例:

// 協議語法
protocol SomeProtocol {
    // 這裏是協議的定義部分
}

struct SomeStructure: FirstProtocol, AnotherProtocol {
 // 這裏是結構體的定義部分
}

class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
 // 這裏是類的定義部分
}

protocol SomeProtocol {
 var mustBeSettable: Int { get set }
 var doesNotNeedToBeSettable: Int { get }
}

protocol AnotherProtocol {
 static var someTypeProperty: Int { get set }
}

protocol FullyNamed {
 var fullName: String { get }
}

struct person: FullyNamed {
 var fullName: String
}
let john = Person(fullName: "John Appleseed")
// john.fullName 爲 "John Appleseed"

class Starship: FullyNamed {
 var prefix: String?
 var name: String
 init(name: String, prefix: String? = nil) {
  self.name = name
  self.prefix = prefix
 }
 
 var fullName: String {
  return (prefix != nil ? prefix! + " " : "") + name
 }
}
var ncc1701 = Starship(name: "Enterprise", prefix: "USS")
// ncc1701.fullName 爲 "USS Enterprise"

泛型

泛型代碼讓你能根據自定義的需求,編寫出適用於任意類型的、靈活可複用的函數及類型。
你可避免編寫重複的代碼,而是用一種清晰抽象的方式來表達代碼的意圖。

泛型函數,類型參數,命名類型參數,泛型類型,泛型擴展,類型約束,關聯類型

示例:

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}

func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}

func swapTwoInts(_ a: inout Int, _ b: inout Int)
func swapTwoValues<T>(_ a: inout T, _ b: inout T)

var someInt = 3
var anotherInt = 107
swapTwoValues(&someInt, &anotherInt)
// someInt 如今是 107,anotherInt 如今是 3

var someString = "hello"
var anotherString = "world"
swapTwoValues(&someString, &anotherString)
// someString 如今是「world」,anotherString 如今是「hello」

不透明類型

具備不透明返回類型的函數或方法會隱藏返回值的類型信息。
函數再也不提供具體的類型做爲返回類型,而是根據它支持的協議來描述返回值。

不透明類型解決的問題,返回不透明類型,不透明類型和協議類型的區別

在處理模塊和調用代碼之間的關係時,隱藏類型信息很是有用,由於返回的底層數據類型仍然能夠保持私有。
不透明類型和泛型相反。不透明類型容許函數實現時,選擇一個與調用代碼無關的返回類型。
若是函數中有多個地方返回了不透明類型,那麼全部可能的返回值都必須是同一類型。返回不透明類型和返回協議類型主要區別,就在因而否須要保證類型一致性。
一個不透明類型只能對應一個具體的類型,即使函數調用者並不能知道是哪種類型;協議類型能夠同時對應多個類型,只要它們都遵循同一協議。
示例:

protocol Shape {
    func draw() -> String
}

struct Triangle: Shape {
    var size: Int
    func draw() -> String {
        var result = [String]()
        for length in 1...size {
            result.append(String(repeating: "*", count: length))
        }
        return result.joined(separator: "\n")
    }
}
let smallTriangle = Triangle(size: 3)
print(smallTriangle.draw())
// *
// **
// ***

struct FlippedShape<T: Shape>: Shape {
    var shape: T
    func draw() -> String {
        let lines = shape.draw().split(separator: "\n")
        return lines.reversed().joined(separator: "\n")
    }
}
let flippedTriangle = FlippedShape(shape: smallTriangle)
print(flippedTriangle.draw())
// ***
// **
// *

自動引用計數

Swift 使用自動引用計數(ARC)機制來跟蹤和管理你的應用程序的內存。
若是兩個類實例互相持有對方的強引用,於是每一個實例都讓對方一直存在,就是這種狀況。這就是所謂的循環強引用。
Swift提供了兩種辦法用來解決你在使用類的屬性時所遇到的循環強引用問題:弱引用(weak reference)和無主引用(unowned reference)。

  • 聲明屬性或者變量時,在前面加上 weak 關鍵字代表這是一個弱引用。

  • 聲明屬性或者變量時,在前面加上關鍵字 unowned 表示這是一個無主引用。

示例:

// 自動引用計數實踐
class Person {
    let name: String
    init(name: String) {
        self.name = name
        print("\(name) is being initialized")
    }
    deinit {
        print("\(name) is being deinitialized")
    }
}

var reference1: Person?
var reference2: Person?
var reference3: Person?
reference1 = Person(name: "John Appleseed")
// 打印「John Appleseed is being initialized」
reference2 = reference1
reference3 = reference1
reference1 = nil
reference2 = nil
reference3 = nil
// 打印「John Appleseed is being deinitialized」

// 循環強引用
class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { print("\(name) is being deinitialized") }
}

class Apartment {
    let unit: String
    init(unit: String) { self.unit = unit }
    var tenant: Person?
    deinit { print("Apartment \(unit) is being deinitialized") }
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")

john = nil
unit4A = nil

// 弱引用
class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { print("\(name) is being deinitialized") }
}

class Apartment {
    let unit: String
    init(unit: String) { self.unit = unit }
    weak var tenant: Person?
    deinit { print("Apartment \(unit) is being deinitialized") }
}

var john: Person?
var unit4A: Apartment?

john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")

john!.apartment = unit4A
unit4A!.tenant = john

john = nil
// 打印「John Appleseed is being deinitialized」

內存安全

默認狀況下,Swift 會阻止你代碼裏不安全的行爲。

理解內存訪問衝突,In-Out 參數的訪問衝突,方法裏 self 的訪問衝突,屬性的訪問衝突

示例:

func balance(_ x: inout Int, _ y: inout Int) {
    let sum = x + y
    x = sum / 2
    y = sum - x
}
var playerOneScore = 42
var playerTwoScore = 30
balance(&playerOneScore, &playerTwoScore)  // 正常
balance(&playerOneScore, &playerOneScore)
// 錯誤:playerOneScore 訪問衝突

訪問控制

訪問控制能夠限定其它源文件或模塊對你的代碼的訪問。

  • open 和 public 級別可讓實體被同一模塊源文件中的全部實體訪問,在模塊外也能夠經過導入該模塊來訪問源文件裏的全部實體。一般狀況下,你會使用 open 或 public 級別來指定框架的外部接口。

  • internal 級別讓實體被同一模塊源文件中的任何實體訪問,可是不能被模塊外的實體訪問。一般狀況下,若是某個接口只在應用程序或框架內部使用,就能夠將其設置爲 internal 級別。

  • fileprivate 限制實體只能在其定義的文件內部訪問。若是功能的部分實現細節只須要在文件內使用時,可使用 fileprivate 來將其隱藏。

  • private 限制實體只能在其定義的做用域,以及同一文件內的 extension 訪問。若是功能的部分細節只須要在當前做用域內使用時,可使用 private 來將其隱藏。

open 爲最高訪問級別(限制最少),private 爲最低訪問級別(限制最多)。
open 只能做用於類和類的成員,它和 public 的區別主要在於 open 限定的類和成員可以在模塊外能被繼承和重寫。
示例:

public class SomePublicClass {}
internal class SomeInternalClass {}
fileprivate class SomeFilePrivateClass {}
private class SomePrivateClass {}

class SomeInternalClass {}   // 隱式 internal
var someInternalConstant = 0 // 隱式 internal

public class SomePublicClass {                  // 顯式 public 類
    public var somePublicProperty = 0            // 顯式 public 類成員
    var someInternalProperty = 0                 // 隱式 internal 類成員
    fileprivate func someFilePrivateMethod() {}  // 顯式 fileprivate 類成員
    private func somePrivateMethod() {}          // 顯式 private 類成員
}

class SomeInternalClass {                       // 隱式 internal 類
    var someInternalProperty = 0                 // 隱式 internal 類成員
    fileprivate func someFilePrivateMethod() {}  // 顯式 fileprivate 類成員
    private func somePrivateMethod() {}          // 顯式 private 類成員
}

fileprivate class SomeFilePrivateClass {        // 顯式 fileprivate 類
    func someFilePrivateMethod() {}              // 隱式 fileprivate 類成員
    private func somePrivateMethod() {}          // 顯式 private 類成員
}

private class SomePrivateClass {                // 顯式 private 類
    func somePrivateMethod() {}                  // 隱式 private 類成員
}

高級運算符

Swift還提供了數種能夠對數值進行復雜運算的高級運算符。它們包含了位運算符和移位運算符。

位運算符、溢出運算符、優先級和結合性、運算符函數、自定義運算符

示例:

let initialBits: UInt8 = 0b00001111
let invertedBits = ~initialBits // 等於 0b11110000

var potentialOverflow = Int16.max
// potentialOverflow 的值是 32767,這是 Int16 能容納的最大整數
potentialOverflow += 1
// 這裏會報錯

struct Vector2D {
    var x = 0.0, y = 0.0
}

extension Vector2D {
    static func + (left: Vector2D, right: Vector2D) -> Vector2D {
        return Vector2D(x: left.x + right.x, y: left.y + right.y)
    }
}

let vector = Vector2D(x: 3.0, y: 1.0)
let anotherVector = Vector2D(x: 2.0, y: 4.0)
let combinedVector = vector + anotherVector
// combinedVector 是一個新的 Vector2D 實例,值爲 (5.0, 5.0)

更多內容

本文分享自微信公衆號 - 悟空聊架構(PassJava666)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索