The Swift Programming Language 重讀筆記

前言

SwiftGG 翻譯組的 《The Swift Programming Language》in Chinese 我在 Swift3 的時(shí)候通讀過(guò)一遍,在 Swift4 的時(shí)候只瀏覽了前半部分,并未通讀?,F(xiàn)在 Swift5 準(zhǔn)備再重新讀一遍文檔,把一些遺忘的、易忘的、感覺(jué)值得記下的知識(shí)點(diǎn)梳理一遍,以方便記憶。

基礎(chǔ)部分

  1. 一般來(lái)說(shuō),很少需要寫(xiě)類(lèi)型注釋(type animation),如果在聲明常量或者變量時(shí)賦了一個(gè)初始值, Swift 可以推斷出這個(gè)常量或變量的類(lèi)型。

  2. 如果需要使用 Swift 保留關(guān)鍵字相同的名稱(chēng)作為常量或變量名,可以使用反引號(hào)將關(guān)鍵字包圍的方式將其作為名字使用。無(wú)論如何,我們都應(yīng)當(dāng)避免使用關(guān)鍵字作為常量或變量名,除非別無(wú)選擇。

  3. 當(dāng)遇到一些相關(guān)值得簡(jiǎn)單分組時(shí),元組是很有用的。元組不適合用來(lái)創(chuàng)建復(fù)雜的數(shù)據(jù)結(jié)構(gòu)。如果數(shù)據(jù)結(jié)構(gòu)比較復(fù)雜,不要使用元組,用類(lèi)或者結(jié)構(gòu)體去建模。

  4. ifguard 用戶(hù)區(qū)別:在 if 條件語(yǔ)句中使用常量或變量來(lái)創(chuàng)建一個(gè)可選綁定,僅在 if 語(yǔ)句的句中 body 中才能獲取到值。在 guard 語(yǔ)句中使用常量或變量創(chuàng)建一個(gè)可選綁定時(shí),僅在 guard 語(yǔ)句外且在語(yǔ)句后才能獲取到值。

  5. 斷言和先決條件的不同點(diǎn)是,他們什么時(shí)候進(jìn)行狀態(tài)檢測(cè):斷言?xún)H在調(diào)試環(huán)境運(yùn)行,而先決條件則在調(diào)試環(huán)境和生產(chǎn)環(huán)境中運(yùn)行。在生產(chǎn)環(huán)境中,斷言的條件將不進(jìn)行評(píng)估。這意味著我們可以再開(kāi)發(fā)階段使用很多斷言,但是這些斷言再生產(chǎn)環(huán)境都不會(huì)產(chǎn)生影響。

基本運(yùn)算符

  1. 在對(duì)負(fù)數(shù) b 求余時(shí),b 的符號(hào)會(huì)被忽略。這意味著 a % -ba % b 是一樣的結(jié)果。但是 -a % ba % b 結(jié)果并不一樣。

  2. 一元正負(fù)號(hào) + 不做任何改變的返回操作數(shù)的值。 雖然一元操作符什么都不會(huì)改變,但當(dāng)使用一元負(fù)號(hào) - 來(lái)表達(dá)負(fù)數(shù)時(shí),可以使用一元正好來(lái)表達(dá)正數(shù),使代嗎具有對(duì)稱(chēng)美。

     let minusSix = -6
     let alsoMinusSix = +minusSix  // alsoMinusSix 等于 -6
    
  3. 如果兩個(gè)元組的元素相同,且長(zhǎng)度相同的話,元組就可以被比較,比較元組大小會(huì)按照從左到右、逐值比較的方式,直到發(fā)現(xiàn)兩個(gè)值不相等時(shí)停止,如果所有值都相等,那么我們就稱(chēng)這一對(duì)元組是相等的。Swift 標(biāo)準(zhǔn)庫(kù)只能比較七個(gè)以?xún)?nèi)元素的元組。如果元組元素超過(guò)七個(gè),則需要自己實(shí)現(xiàn)比較運(yùn)算符。

    (1, "zebra") < (2, "apple")   // true,因?yàn)?1 小于 2
    (3, "apple") < (3, "bird")    // true,因?yàn)?3 等于 3,但是apple 小于 bird
    (4, "dog") == (4, "dog")      // true,因?yàn)?4 等于 4,dog 等于 dog
    
  1. 空合運(yùn)算符 ??。ps. 其實(shí)我一直我不清除這個(gè)叫啥名字。。

字符串和字符

  1. 多行字符串字面量,是由一對(duì)三個(gè)雙引號(hào)包裹著的具有固定順序的文本字符集。也可以在行尾寫(xiě)一個(gè)反斜杠 \ 作為續(xù)行符。

      let softWrappedQuotation = """
         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."
         """
    
  1. 關(guān)于 字符串字面量的特殊字符,主要是轉(zhuǎn)義字符、Unicode 標(biāo)量。平時(shí)使用較少。還有轉(zhuǎn)義字符反斜杠 \ 的使用。
  1. 擴(kuò)展字符串分隔符 #。將字符串文字放在擴(kuò)展分隔符中,這樣字符串中的特殊字符將會(huì)被直接包含而不是轉(zhuǎn)移后的效果。如果想要轉(zhuǎn)義字符效果,用等量 # 包裹住即可。

  2. 字符串、結(jié)構(gòu)體、枚舉,都是值類(lèi)型。

  3. 字符串知識(shí)中,關(guān)于 Unicode ,比較零碎,實(shí)際開(kāi)發(fā)中也很少用到,已讀。

  4. 關(guān)于計(jì)算字符數(shù)量,需要注意的是通過(guò) count 屬性返回的字符數(shù)量并不總是與包含相同自負(fù)的 NSStringlength 屬性相同。 NSStringlength 屬性是利用 UTF-16 表示的十六位代碼單元數(shù)字,而不是 Unicode 可擴(kuò)展的字符群集。

  5. 不同的字符可能會(huì)占用不同數(shù)量的存儲(chǔ)空間,要以要知道字符串中 Character 的確定位置,就必須從 String 開(kāi)頭遍歷每一個(gè) Unicode 標(biāo)量到結(jié)尾。因此, Swift 的字符串不能整數(shù) integer 做索引。

  6. 子字符串 SubStringString 區(qū)別在于性能優(yōu)化上。SubString 可以重用原 String 的內(nèi)存空間,或者另一個(gè) SubString 的內(nèi)存空間(String 也有同樣的優(yōu)化,但如果兩個(gè) String 共享內(nèi)存的話,它們就會(huì)相等)。這一優(yōu)化意味著你在修改 StringSubString 之前都不需要消耗性能去復(fù)制內(nèi)存。就像前面說(shuō)的那樣,SubString 不適合長(zhǎng)期存儲(chǔ) —— 因?yàn)樗赜昧嗽?String 的內(nèi)存空間,原 String 的內(nèi)存空間必須保留直到它的 SubString 不再被使用為止。

集合類(lèi)型

  1. 關(guān)于數(shù)組,每天都在用,要強(qiáng)調(diào)的不多。這里說(shuō)三個(gè)方法及區(qū)別:removeLast() ,移除數(shù)組最后一個(gè)元素并返回該元素,數(shù)組必須不為空,否則會(huì)引發(fā)運(yùn)行時(shí)崩潰; popLast() ,移除數(shù)組最后一個(gè)可選類(lèi)型的元素,也就是說(shuō),數(shù)組可以為空; dropLast() ,移除掉最后幾個(gè)元素,并返回移除后的原數(shù)組。

    var digitArr = [1,2,3,4,5]
    var last1 = digitArr.removeLast()
    var last2 = digitArr.popLast()
    var remain = digitArr.dropLast(2)
    print(last1, last2 ?? "-2", remain)
    // 輸出: 5 4 [1]
    
  2. 集合 Set。用來(lái)存儲(chǔ)相同類(lèi)型并且沒(méi)有順序的值。當(dāng)集合元素順序不重要并且確保每個(gè)元素只出現(xiàn)一次時(shí),可以使用集合而不是數(shù)組??勺约憾x類(lèi)型作為集合的值類(lèi)型,但是定義的類(lèi)型要遵循 Hasshable 協(xié)議,而 Hashable 協(xié)議遵循了 Equatable 協(xié)議,所以定義的類(lèi)型符合這些協(xié)議即可。

  3. 集合提供了一些方法可以高效完成對(duì)集合的操作。

    • 使用 intersection(_:) 方法根據(jù)兩個(gè)集合的交集創(chuàng)建一個(gè)新的集合。
    • 使用 symmetricDifference(_:) 方法根據(jù)兩個(gè)集合不相交的值創(chuàng)建一個(gè)新的集合。
    • 使用 union(_:) 方法根據(jù)兩個(gè)集合的所有值創(chuàng)建一個(gè)新的集合。
    • 使用 subtracting(_:) 方法根據(jù)不在另一個(gè)集合中的值創(chuàng)建一個(gè)新的集合。
  4. 集合還提供了方法來(lái)判斷集合之間的關(guān)系:

    • 使用“是否相等”運(yùn)算符 == 來(lái)判斷兩個(gè)集合包含的值是否全部相同。
    • 使用 isSubset(of:) 方法來(lái)判斷一個(gè)集合中的所有值是否也被包含在另外一個(gè)集合中。
    • 使用 isSuperset(of:) 方法來(lái)判斷一個(gè)集合是否包含另一個(gè)集合中所有的值。
    • 使用 isStrictSubset(of:) 或者 isStrictSuperset(of:) 方法來(lái)判斷一個(gè)集合是否是另外一個(gè)集合的子集合或者父集合并且兩個(gè)集合并不相等。
    • 使用 isDisjoint(with:) 方法來(lái)判斷兩個(gè)集合是否不含有相同的值(是否沒(méi)有交集)。
  1. 字典 Dictionary 是一個(gè)無(wú)序集合,存儲(chǔ)鍵值之間的關(guān)系。為了以特定的順序遍歷字典的鍵或值,可以對(duì)字典的 keysvalues 使用 sorted() 方法。如果想要移除字典中的相關(guān)元素,可以可以使用如下兩種方式:

    var ages = ["Tom": 20, "jack": 12, "Lilei": 18]
    ages.removeValue(forKey: "Tom")
    print(ages)
    /// ["Lilei": 18, "jack": 12]
    ages["KK"] = nil
    print(ages)
    /// ["Lilei": 18, "jack": 12]
    ages["jack"] = nil
    print(ages)
    /// ["Lilei": 18]
    

控制流

  1. Swift 提供了多種流程控制結(jié)構(gòu),包括可以多次執(zhí)行任務(wù)的 While 循環(huán),基于特定條件選擇執(zhí)行不同代碼分支的 if 、 guard 、 Switch 語(yǔ)句,還有控制流程跳轉(zhuǎn)到其他位置的 breakcontinue 語(yǔ)句。

    Swift 還提供了 for-in 循環(huán),用來(lái)更簡(jiǎn)單的遍歷數(shù)組、字典、區(qū)間、字符串和其他序列類(lèi)型。

  2. for-in 循環(huán)中,值得注意的有一點(diǎn): stride(from:to:by:) 用于半開(kāi)半閉區(qū)間, stride(from:through:by:) 用于閉區(qū)間,都是間隔循環(huán)。示例如下:

    let minutes = 15
    let minuteInterval = 5
    for tickMark in stride(from: 0, to: minutes, by: minuteInterval) {
        print("tickMark = ", tickMark)
    }
    /// 輸出: 0  5  10
    for tickMark in stride(from: 0, through: minutes, by: minuteInterval) {
        print("tickMark2 = ", tickMark)
    }
    /// 輸出: 0  5  10  15
    
  3. while 循環(huán)從計(jì)算一個(gè)條件開(kāi)始,如果條件為 true ,會(huì)重復(fù)運(yùn)行一段代碼,直到條件變?yōu)?false。repeat-whilewhile 的區(qū)別在于判斷循環(huán)條件之前,先執(zhí)行一次循環(huán)的代碼塊。然后重復(fù)循環(huán)直到條件為 false。

  4. SwitchSwift 中是非常強(qiáng)大、靈活的。支持區(qū)間匹配、元組匹配、值綁定、where 條件、復(fù)合型 case 以及包含相同值綁定的復(fù)合型 case 等。

  5. Swift 共有五種控制轉(zhuǎn)移語(yǔ)句:
    continue,break``fallthroughreturn,throw

    • continue :告訴一個(gè)循環(huán)立即停止本次循環(huán),重新開(kāi)始下一次的循環(huán)。但是不會(huì)離開(kāi)整個(gè)循環(huán)。

    • break:立刻結(jié)束整個(gè)控制流的執(zhí)行。 break 在循環(huán)中,是結(jié)束該循環(huán)。在 Switch 中,是結(jié)束該 Switch

    • fallthrough:在 switch 中使用,該關(guān)鍵字不會(huì)檢查它下一個(gè)將會(huì)落入執(zhí)行的 case 中的匹配條件。fallthrough 簡(jiǎn)單地使代碼繼續(xù)連接到下一個(gè) case 中的代碼。這和 C 語(yǔ)言標(biāo)準(zhǔn)中的 switch 特性是一樣的。

  1. 帶標(biāo)簽的語(yǔ)句。這個(gè)我個(gè)人確實(shí)沒(méi)有用到過(guò)。這里記一下。簡(jiǎn)單來(lái)說(shuō),就是給語(yǔ)句命名,然后控制轉(zhuǎn)移語(yǔ)句直接針對(duì)特性語(yǔ)句做處理,下面是代碼:

    let finalSquare = 25
    var board = [Int](repeating: 0, count: finalSquare + 1)
    board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
    board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
    var square = 0
    var diceRoll = 0
    
    gameLoop: while square != finalSquare {
        diceRoll += 1
        if diceRoll == 7 { diceRoll = 1 }
        switch square + diceRoll {
        case finalSquare:
            // 骰子數(shù)剛好使玩家移動(dòng)到最終的方格里,游戲結(jié)束。
            break gameLoop
        case let newSquare where newSquare > finalSquare:
            // 骰子數(shù)將會(huì)使玩家的移動(dòng)超出最后的方格,那么這種移動(dòng)是不合法的,玩家需要重新擲骰子
            continue gameLoop
        default:
            // 合法移動(dòng),做正常的處理
            square += diceRoll
            square += board[square]
        }
    }
    print("Game over!")
    
  2. 提前退出語(yǔ)句 guard 。

  3. 檢測(cè) API 可用性,只要是針對(duì)不同系統(tǒng)版本的兼容。通用寫(xiě)法:

    if #available(平臺(tái)名稱(chēng) 版本號(hào), ..., *) {
        APIs 可用,語(yǔ)句將執(zhí)行
    } else {
        APIs 不可用,語(yǔ)句將不執(zhí)行
    }
    

    實(shí)際案例展示:

    if #available(iOS 10, macOS 10.12, *) {
        // 在 iOS 使用 iOS 10 的 API, 在 macOS 使用 macOS 10.12 的 API
    } else {
        // 使用先前版本的 iOS 和 macOS 的 API
    }
    

函數(shù)

  1. 函數(shù)的可變參數(shù)。一個(gè)可變參數(shù) variadic parameter 可以接受零個(gè)或多個(gè)值。通過(guò)在變量類(lèi)型名后面加入 ... 的方式來(lái)定義可變參數(shù)。傳入值在函數(shù)體中變?yōu)榇祟?lèi)型的一個(gè)數(shù)組。一個(gè)函數(shù)中最多只能有一個(gè)可變參數(shù)。

    func arithmeticMean(_ numbers: Double...) -> Double {
        var total: Double = 0
        for number in numbers {
            total += number
        }
        return total / Double(numbers.count)
    }
    arithmeticMean(1, 2, 3, 4, 5)
    // 返回 3.0, 是這 5 個(gè)數(shù)的平均數(shù)。
    arithmeticMean(3, 8.25, 18.75)
    // 返回 10.0, 是這 3 個(gè)數(shù)的平均數(shù)。
    
  2. 定義輸入輸出函數(shù),在參數(shù)定義前加 inout 關(guān)鍵字即可。注意,只能傳變量給輸入輸出參數(shù),不能傳常量或字面量,因?yàn)檫@些量是不能被修改的。輸入輸出參數(shù)不能有默認(rèn)值,而且可變參數(shù)不能使用 inout 標(biāo)記。

閉包

  1. 關(guān)于閉包表達(dá)式的進(jìn)化。雖然越變?cè)胶?jiǎn)單,但是,還是應(yīng)該以清晰明了為主。示例只是對(duì)閉包語(yǔ)法的一個(gè)說(shuō)明:

    /// 排序數(shù)組
    let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
    /// 1.0 版本
    func backward(_ s1: String, _ s2: String) -> Bool {
        return s1 > s2
    }
    var reversedNames = names.sorted(by: backward)
    print(reversedNames)
    // reversedNames 為 ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
    
    /// 2.0 版本
    reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
        return s1 > s2
    })
    
    /// 3.0 版本
    reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } )
    
    /// 4.0 版本
    reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
    
    /// 5.0 版本
    reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
    
    /// 6.0 版本
    reversedNames = names.sorted(by: { $0 > $1 } )
    
    /// 最終版
    reversedNames = names.sorted(by: >)
    
  2. 閉包是引用類(lèi)型??!

  3. 在逃逸閉包內(nèi)必須顯示的調(diào)用 self ,非逃逸閉包則必須要。所謂逃逸閉包,就是指函數(shù)返回后才會(huì)執(zhí)行的閉包。典型的就是網(wǎng)絡(luò)閉包,在請(qǐng)求結(jié)束后才會(huì)執(zhí)行。還有自動(dòng)閉包 autoclosure ,但是這個(gè)并不建議使用過(guò)多。

    override func viewDidLoad() {
        super.viewDidLoad()
        
        someFunctionWithNonescapingClosure {
            x = 50
        }
        someFunctionWithEscapingClosure {
            self.x = 100
        }
        someFuncationWithAutoclosureClosure(closure: "Hello")
    }
    
    /// 非逃逸閉包
    func someFunctionWithNonescapingClosure(closure: () -> Void) {
         closure()
    }
    /// 逃逸閉包
    func someFunctionWithEscapingClosure(closure: @escaping () -> Void) {
        closure()
    }
    /// 自動(dòng)轉(zhuǎn)化閉包
    func someFuncationWithAutoclosureClosure(closure: @autoclosure () -> String) {
        print("\(closure())")
    }
    

枚舉

  1. 令枚舉遵循 CaseIterable 協(xié)議。Swift 會(huì)生成一個(gè) allCases 屬性,用于一個(gè)包含枚舉所有成員的集合。如:

    enum Beverage: CaseIterable {
        case coffee, tea, juice
    }
    let numberOfChoices = Beverage.allCases.count
    print("\(numberOfChoices) beverages available")
    // 打印“3 beverages available”
    
  2. 遞歸枚舉是一種枚舉類(lèi)型,它由一個(gè)或多個(gè)枚舉成員使用該枚舉類(lèi)型的實(shí)例作為關(guān)聯(lián)值。用遞歸枚舉時(shí),編譯器會(huì)插入一個(gè)間接層??梢栽诿杜e成員前加上 indirect 來(lái)標(biāo)識(shí)該成員可遞歸。

    例如,下面的例子,枚舉存儲(chǔ)了簡(jiǎn)單的算術(shù)表達(dá)式:

    enum ArithmeticExpression {
        case number(Int)
        indirect case addition(ArithmeticExpression, ArithmeticExpression)
        indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
    }
    

    也可以再枚舉類(lèi)型開(kāi)頭加上 indirect 關(guān)鍵字來(lái)表明它的所有成員都是可遞歸的:

    indirect enum ArithmeticExpression {
        case number(Int)
        case addition(ArithmeticExpression, ArithmeticExpression)
        case multiplication(ArithmeticExpression, ArithmeticExpression)
    }
    

類(lèi)和結(jié)構(gòu)體

  1. Swift 中機(jī)構(gòu)體和類(lèi)有很多共同點(diǎn),兩者都可以:

    • 定義屬性有存儲(chǔ)值
    • 定義方法用于提供功能
    • 定義下標(biāo)操作用于通過(guò)下標(biāo)訪問(wèn)它們的值
    • 定義構(gòu)造器用于生成初始值
    • 通過(guò)擴(kuò)展增加默認(rèn)實(shí)現(xiàn)以外的功能
    • 遵循協(xié)議提供某些標(biāo)準(zhǔn)功能

    與結(jié)構(gòu)體相比,類(lèi)還有如下的附加功能:

    • 繼承,允許一個(gè)類(lèi)繼承另一個(gè)類(lèi)的特征
    • 類(lèi)型轉(zhuǎn)換,允許在運(yùn)行時(shí)檢查和解釋一個(gè)類(lèi)實(shí)例的類(lèi)型
    • 析構(gòu)器,允許一個(gè)類(lèi)實(shí)例釋放任務(wù)其所被分配的資源
    • 引用計(jì)數(shù),允許對(duì)一個(gè)類(lèi)多次引用

    類(lèi)增加的附加功能是以增加復(fù)雜性為代價(jià)的,作為一般準(zhǔn)則,優(yōu)先使用結(jié)構(gòu)體,因?yàn)樗鼈兏菀桌斫?,僅在適當(dāng)或必要時(shí)使用類(lèi)。實(shí)際上,這意味著我們大多數(shù)自定義的數(shù)據(jù)類(lèi)型都應(yīng)該是結(jié)構(gòu)體和枚舉。

  2. 恒等運(yùn)算符

    因?yàn)轭?lèi)是引用類(lèi)型,所以多個(gè)常量或變量幕后可能引用同一個(gè)類(lèi)實(shí)例。對(duì)于結(jié)構(gòu)體和枚舉來(lái)說(shuō),這并不成立,因?yàn)樗麄兌际侵殿?lèi)型,再被賦予到常量、變量或者傳遞到函數(shù)時(shí),其值總是會(huì)被拷貝。

    判定兩個(gè)常量或者變量是否引用同一個(gè)類(lèi)實(shí)例又是會(huì)非常有用。為了達(dá)到這個(gè)目的,Swift 提供了兩個(gè)恒等運(yùn)算符,使用這兩個(gè)運(yùn)算符檢測(cè)兩個(gè)常量或者變量是否引用同一個(gè)類(lèi)實(shí)例:

    • 相同 ===
    • 不相同 !==

    請(qǐng)注意,相同 (用三個(gè)等號(hào)標(biāo)識(shí) ===) 與 相等 (用兩個(gè)等號(hào)表示 ==) 的不同,相同 表示兩個(gè)類(lèi)類(lèi)型(class type) 的常量或變量是否引用同一個(gè)類(lèi)實(shí)例,相等 用于表示兩個(gè)值是否相等或等價(jià),判定時(shí)要遵循設(shè)計(jì)者定義的評(píng)判標(biāo)準(zhǔn)。

屬性

  1. 全局的常量或者變量都是延遲計(jì)算的,跟延時(shí)加載存儲(chǔ)屬性相似,不同的地方在于,全局的常量或者變量不需要標(biāo)記 lazy 修飾符。而局部范圍的常量或變量從不延遲計(jì)算。

  2. 類(lèi)型屬性,不論創(chuàng)建多少一個(gè)類(lèi)的類(lèi)實(shí)例,類(lèi)型屬性都只有唯一一份,所有實(shí)例共享該數(shù)據(jù)。使用 static (用于類(lèi)和結(jié)構(gòu)體),或者 class(用于類(lèi))關(guān)鍵字作為修飾符。

方法

  1. 方法是與某些特性類(lèi)型相關(guān)聯(lián)的函數(shù)。譬如實(shí)例方法、類(lèi)方法等。
  1. 在實(shí)例方法中修改值類(lèi)型

    結(jié)構(gòu)體和枚舉都是值類(lèi)型,默認(rèn)情況下,值類(lèi)型的屬性不能在它的實(shí)例方法中被修改。

    但是,如果確定需要在某個(gè)特定的方法中修改結(jié)構(gòu)體或者枚舉的屬性,可以為這個(gè)方法選擇 可變 mutating 行為,然后就可以在方法內(nèi)部改變它的屬性。

    在可變方法中可以給 self 重新賦值:

    enum TriStateSwitch {
        case off, low, high
        mutating func next() {
            switch self {
            case .off:
                self = .low
            case .low:
                self = .high
            case .high:
                self = .off
            }
        }
    }
    var ovenLight = TriStateSwitch.low
    ovenLight.next()
    // ovenLight 現(xiàn)在等于 .high
    ovenLight.next()
    // ovenLight 現(xiàn)在等于 .off
    

下標(biāo)

  1. 下標(biāo)允許通過(guò)在實(shí)例名稱(chēng)后面的方括號(hào)中傳入一個(gè)或者多個(gè)索引值來(lái)對(duì)實(shí)例進(jìn)行查詢(xún)。它的語(yǔ)法類(lèi)似于實(shí)例方法語(yǔ)法和計(jì)算型屬性語(yǔ)法。定義下標(biāo)使用 subscipt 關(guān)鍵字,與定義實(shí)例方法類(lèi)似,都是指定一個(gè)或多個(gè)輸入?yún)?shù)和一個(gè)返回類(lèi)型。與實(shí)例方法不同的是,下標(biāo)可以設(shè)定為讀寫(xiě)或只讀。這種行為由 gettersetter 實(shí)現(xiàn),類(lèi)似計(jì)算型屬性:

    subscript(index: Int) -> Int {
        get {
             // 返回一個(gè)適當(dāng)?shù)?Int 類(lèi)型的值
         }
        set(newValue) {
            // 執(zhí)行適當(dāng)?shù)馁x值操作
        }
    }
    
  1. 實(shí)例下標(biāo)是在特定類(lèi)型的一個(gè)實(shí)例上調(diào)用的下標(biāo)。也可以定義一種在這個(gè)類(lèi)型自身上調(diào)用的下標(biāo)。這種下標(biāo)被稱(chēng)作類(lèi)型下標(biāo)。可以通過(guò)在 subscript 關(guān)鍵字之前寫(xiě)下 static 關(guān)鍵字的方式來(lái)表示一個(gè)類(lèi)型下標(biāo)。類(lèi)類(lèi)型可以使用 class 關(guān)鍵字來(lái)代替 static,它允許子類(lèi)重寫(xiě)父類(lèi)中對(duì)那個(gè)下標(biāo)的實(shí)現(xiàn)。下面的例子展示了如何定義和調(diào)用一個(gè)類(lèi)型下標(biāo):

    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)
    
  2. 補(bǔ)充一個(gè) staticclass 的區(qū)別:

    • static 可以修飾屬性、函數(shù),可用于類(lèi)或者結(jié)構(gòu)體中。但是被 static 修飾的對(duì)象不支持重寫(xiě) override。
    • class 只用于類(lèi)對(duì)象中,可以重寫(xiě)。

繼承

  1. 在合適的地方,可以通過(guò)使用 super 前綴來(lái)訪問(wèn)超類(lèi)版本的方法、屬性或者下標(biāo):
    • 在方法 someMetho() 的重寫(xiě)實(shí)現(xiàn)中,可以通過(guò) super.someMethod 來(lái)調(diào)用超類(lèi)版本的 someMethod() 方法。
    • 在屬性 somePropertygettersetter 的重寫(xiě)實(shí)現(xiàn)中,可以通過(guò) super.someProperty 來(lái)訪問(wèn)超類(lèi)版本的 someProperty 屬性。
    • 在下標(biāo)的重寫(xiě)實(shí)現(xiàn)中,可以通過(guò) super[someIndex] 來(lái)訪問(wèn)超類(lèi)版本中相同的下標(biāo)。
  1. 可以把方法、屬性、下標(biāo)標(biāo)記為 final 來(lái)防止它們被重寫(xiě)。只需要在聲明關(guān)鍵字前加上 final 修飾符即可。也可以在關(guān)鍵字 class 前添加 final 修飾符,來(lái)將整個(gè)類(lèi)標(biāo)記為 final 。這樣的類(lèi)是不可以被繼承的。

構(gòu)造過(guò)程

  1. 類(lèi)和結(jié)構(gòu)體在創(chuàng)建實(shí)例時(shí),必須為所有存儲(chǔ)屬性設(shè)置合適的初始值。存儲(chǔ)屬性不能處于一個(gè)未知的狀態(tài)。當(dāng)為存儲(chǔ)屬性設(shè)置默認(rèn)值或者在構(gòu)造器中設(shè)置初始值時(shí),他們的值是被直接設(shè)置的,不會(huì)觸發(fā)任何屬性觀察者。
  1. 關(guān)于 形參命名實(shí)參標(biāo)簽

    下面代碼塊中, fromFahrenheit 是實(shí)參標(biāo)簽,fahrenheit 是形參命名。

    struct Celsius {
        var temperatureInCelsius: Double
        init(fromFahrenheit fahrenheit: Double) {
            temperatureInCelsius = (fahrenheit - 32.0) / 1.8
        }
    }
    
  2. 可以再構(gòu)造過(guò)程中的任意點(diǎn)給常量屬性賦值,只要在構(gòu)造過(guò)程結(jié)束時(shí)它設(shè)置成正確的值。一旦長(zhǎng)兩屬性被賦值,它將永遠(yuǎn)不能更改。對(duì)于類(lèi)實(shí)例來(lái)說(shuō),它的常量屬性只能在類(lèi)的構(gòu)造過(guò)程中被修改,不能在子類(lèi)中修改。

  3. 結(jié)構(gòu)體如果沒(méi)有定義任何自定義構(gòu)造器,它將自動(dòng)獲得一個(gè) 逐一成員構(gòu)造器(memberwise initializer)。不像默認(rèn)構(gòu)造器,空手存儲(chǔ)型屬性有默認(rèn)值,結(jié)構(gòu)體也會(huì)獲得逐一成員構(gòu)造器。

  4. 構(gòu)造器可以調(diào)用其他構(gòu)造器來(lái)完成實(shí)力部分的構(gòu)造過(guò)程。這一過(guò)程成為構(gòu)造器代理。

  5. Swift 為類(lèi)提供了兩種構(gòu)造器來(lái)確保實(shí)例中所有的存儲(chǔ)型屬性都能獲得初始值,它們被稱(chēng)為指定構(gòu)造器和便利構(gòu)造器。指定構(gòu)造器是類(lèi)中最主要的構(gòu)造器。其中:

    • 指定構(gòu)造器必須總是 向上代理
    • 便利構(gòu)造器必須總是 橫向代理
  6. 安全檢查的兩段式構(gòu)造過(guò)程展示:

    階段 1

    • 類(lèi)的某個(gè)指定構(gòu)造器或便利構(gòu)造器被調(diào)用。
    • 完成類(lèi)的新實(shí)例的內(nèi)存分配,但此時(shí)內(nèi)存還沒(méi)有被初始化。
    • 指定構(gòu)造器確保其所在的類(lèi)引入的所有存儲(chǔ)型屬性都已賦初值。存儲(chǔ)型屬性的內(nèi)存完成初始化。
    • 指定構(gòu)造器切換到父類(lèi)的構(gòu)造器,對(duì)其存儲(chǔ)屬性完成同樣的任務(wù)。
    • 這個(gè)過(guò)程沿著類(lèi)的繼承鏈一直往上執(zhí)行,直到達(dá)到繼承鏈的最頂部。
    • 當(dāng)達(dá)到了繼承鏈的最頂部,而且繼承鏈的最后一個(gè)類(lèi)已經(jīng)確保其所有的存儲(chǔ)型屬性已賦初值,這個(gè)實(shí)例的內(nèi)存被認(rèn)為已經(jīng)完全初始化,此時(shí),階段1完成。

    階段 2

    • 從繼承鏈的頂部往下,繼承鏈上每個(gè)類(lèi)的指定構(gòu)造器都有機(jī)會(huì)進(jìn)一步自定義實(shí)例。構(gòu)造器此時(shí)可訪問(wèn) self ,修改它的屬性并調(diào)用實(shí)例方法等。
    • 最終,繼承鏈中任意的便利構(gòu)造器都有機(jī)會(huì)自定義其實(shí)例和使用 self 。
  7. 可失敗構(gòu)造器的參數(shù)名和參數(shù)類(lèi)型,不能與其他非可失敗構(gòu)造器的參數(shù)名,及參數(shù)類(lèi)型相同。嚴(yán)格來(lái)說(shuō),構(gòu)造器都不支持返回值。因?yàn)闃?gòu)造器本身的作用,只是確保對(duì)象能被正確的構(gòu)造。因此只是用 return nil 來(lái)可失敗構(gòu)造器構(gòu)造失敗,而不要用關(guān)鍵字 return 來(lái)表明構(gòu)造成功。

    struct Animal {
        let species: String
        init?(species: String) {
            if species.isEmpty {
                return nil
            }
            self.species = species
        }
    }
    

析構(gòu)過(guò)程

  1. 析構(gòu)器只適用于類(lèi)類(lèi)型,析構(gòu)器是在實(shí)例被釋放前自動(dòng)調(diào)用的。不能主動(dòng)調(diào)用析構(gòu)器。子類(lèi)繼承父類(lèi)的構(gòu)造器,并且在子類(lèi)的構(gòu)造器實(shí)現(xiàn)的最后,父類(lèi)構(gòu)造器會(huì)被自動(dòng)調(diào)用。

可選鏈

  1. 嗯,就是那個(gè) ?。這里只是說(shuō)明了怎么調(diào)用屬性、方法、下標(biāo)等。

錯(cuò)誤處理

  1. Swift 中的錯(cuò)誤處理和其他語(yǔ)言中用 try 、 catch 、 和 throws 進(jìn)行異常處理很像。和其他語(yǔ)言(包括 Objective-C)的異常處理不同的是, Swift 中的錯(cuò)誤處理不涉及解除調(diào)用棧,這是一個(gè)計(jì)算代價(jià)高昂的過(guò)程。就此而言, throw 語(yǔ)句的性能特性可以和 return 語(yǔ)句媲美。
  1. 為了表示一個(gè)函數(shù)、方法或構(gòu)造器可以拋出錯(cuò)誤,在函數(shù)聲明后加上 throws 關(guān)鍵字。一個(gè)標(biāo)有 throws 的函數(shù)稱(chēng)為 throwing 函數(shù)。如果這個(gè)函數(shù)指明了返回類(lèi)型,throws 關(guān)鍵字要寫(xiě)在返回箭頭 -> 的前面。只有 throwing 函數(shù)可以傳遞錯(cuò)誤。任何在非 throwing 函數(shù)內(nèi)部拋出的錯(cuò)誤都只能在函數(shù)內(nèi)部處理。
  2. do-catch 可以對(duì)錯(cuò)誤進(jìn)行匹配。 defer 是將代碼的執(zhí)行延遲到當(dāng)前作用域退出之前。

類(lèi)型轉(zhuǎn)換

  1. 類(lèi)型轉(zhuǎn)換在 Swift 中使用 asis 操作符實(shí)現(xiàn)。
  2. 類(lèi)型檢查操作符 is 來(lái)檢查一個(gè)實(shí)例是否屬于特定子類(lèi)型。 as? 或者 as! 用來(lái)轉(zhuǎn)換。
  3. Swift 為不確定的類(lèi)型提供了兩種類(lèi)型別名:
    • Any 標(biāo)識(shí)任何類(lèi)型,包括函數(shù)類(lèi)型。
    • AnyObject 可以表示任何類(lèi)類(lèi)型的實(shí)例。

嵌套類(lèi)型

  1. 所謂 嵌套類(lèi)型,是指可以在支持的類(lèi)型中定義嵌套的枚舉、類(lèi)和結(jié)構(gòu)體。要在一個(gè)類(lèi)型中嵌套另一個(gè)類(lèi)型,將嵌套類(lèi)型定義在其外部類(lèi)型的 { } 內(nèi)即可,而且根據(jù)需要可以定義多級(jí)嵌套。(根據(jù) SwiftLint 不建議嵌套過(guò)多)

擴(kuò)展

  1. 擴(kuò)展可以給一個(gè)現(xiàn)有類(lèi)、結(jié)構(gòu)體、枚舉,還有協(xié)議添加新的功能。它擁有在不訪問(wèn)擴(kuò)展類(lèi)型源碼實(shí)現(xiàn)的基礎(chǔ)上就完成功能擴(kuò)展的能力,即逆向建模。但需要注意的是,擴(kuò)展可以添加新的功能,但是不能重寫(xiě)已經(jīng)存在的功能。Swift 中的擴(kuò)展可以:
    • 添加計(jì)算型實(shí)力屬性和類(lèi)屬性。
    • 定義實(shí)例方法和類(lèi)方法。
    • 提供新的構(gòu)造器。
    • 定義下標(biāo)。
    • 定義和使用新的嵌套類(lèi)型。
    • 使已經(jīng)存在的類(lèi)型遵循某個(gè)協(xié)議。
  1. 擴(kuò)展可以添加新的計(jì)算屬性,但是不能添加存儲(chǔ)屬性,或著向現(xiàn)有屬性添加屬性觀察者。
  2. 擴(kuò)展可以添加便利構(gòu)造器,但是不能添加指定構(gòu)造器。

協(xié)議

  1. 有時(shí)候需要在方法中改變(或異變)方法所屬的實(shí)例,對(duì)值類(lèi)型,則需要加 mutating 關(guān)鍵字。如果在協(xié)議中定義了一個(gè)實(shí)例方法,該方法會(huì)改變遵循該協(xié)議的類(lèi)型的實(shí)例,那么在定義協(xié)議時(shí)需要在方法前添加 mutating 關(guān)鍵字。這使得結(jié)構(gòu)體和枚舉能夠遵循該協(xié)議并滿(mǎn)足此方法要求。
  2. 協(xié)議合成,即多個(gè)協(xié)議合一通過(guò) & 符號(hào)結(jié)合起來(lái)。用的并不是很多。
  3. 協(xié)議擴(kuò)展,可以更優(yōu)雅的實(shí)現(xiàn) 可選協(xié)議

泛型

  1. 請(qǐng)始終以大寫(xiě)字母開(kāi)頭的駝峰命名法來(lái)為類(lèi)型參數(shù)命名,以表明它是一個(gè)占位類(lèi)型,而不是一個(gè)值。

  2. 類(lèi)型約束指定類(lèi)型參數(shù)必須繼承自指定類(lèi)、遵循特定的協(xié)議或協(xié)議組合。如下:

    func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
        // 這里是泛型函數(shù)的函數(shù)體部分
    }
    
  3. 關(guān)聯(lián)類(lèi)型

    定義一個(gè)協(xié)議時(shí),聲明一個(gè)或多個(gè)關(guān)聯(lián)類(lèi)型作為協(xié)議定義的一部分將會(huì)非常有用。關(guān)聯(lián)類(lèi)型為協(xié)議中某個(gè)類(lèi)型提供了一個(gè)占位符名稱(chēng),其代表的實(shí)際類(lèi)型在協(xié)議被遵循時(shí)才會(huì)指定。關(guān)聯(lián)類(lèi)型通過(guò) associatedtype 關(guān)鍵字來(lái)指定。

    protocol Container {
        associatedtype Item
        mutating func append(_ item: Item)
        var count: Int { get }
        subscript(i: Int) -> Item { get }
    }
    

    下面用一個(gè)結(jié)構(gòu)體來(lái)遵循這個(gè)協(xié)議:

    struct IntStack: Container {
        // IntStack 的原始實(shí)現(xiàn)部分
        var items = [Int]()
        mutating func push(_ item: Int) {
            items.append(item)
        }
        mutating func pop() -> Int {
            return items.removeLast()
        }
        // Container 協(xié)議的實(shí)現(xiàn)部分
        typealias Item = Int
        mutating func append(_ item: Int) {
            self.push(item)
        }
        var count: Int {
            return items.count
        }
        subscript(i: Int) -> Int {
            return items[i]
        }
    }
    

    或者使用一個(gè)泛型 Stack 來(lái)遵循該協(xié)議:

    struct Stack<Element>: Container {
        // Stack<Element> 的原始實(shí)現(xiàn)部分
        var items = [Element]()
        mutating func push(_ item: Element) {
            items.append(item)
        }
        mutating func pop() -> Element {
            return items.removeLast()
        }
        // Container 協(xié)議的實(shí)現(xiàn)部分
        mutating func append(_ item: Element) {
            self.push(item)
        }
        var count: Int {
            return items.count
        }
        subscript(i: Int) -> Element {
            return items[i]
        }
    }
    

    也可以在協(xié)議里給關(guān)聯(lián)類(lèi)型添加約束來(lái)要求遵循的類(lèi)型滿(mǎn)足協(xié)議:

    protocol Container {
        associatedtype Item: Equatable
        mutating func append(_ item: Item)
        var count: Int { get }
        subscript(i: Int) -> Item { get }
    }
    

    或者在關(guān)聯(lián)類(lèi)型約束里使用協(xié)議:

    protocol SuffixableContainer: Container {
        associatedtype Suffix: SuffixableContainer where Suffix.Item == Item
        func suffix(_ size: Int) -> Suffix
    }
    
  4. ** 泛型 Where 語(yǔ)句**

    需要靈活使用 where 進(jìn)行條件約束即可。

不透明類(lèi)型

  1. 雖然使用不透明類(lèi)型作為返回值,看起來(lái)和返回協(xié)議類(lèi)型非常相似,但兩者有個(gè)主要區(qū)別,就是是否需要保持類(lèi)型一致性。一個(gè)不透明類(lèi)型只能對(duì)應(yīng)一種具體的類(lèi)型,即便函數(shù)調(diào)用者并不知道是哪一種類(lèi)型。協(xié)議類(lèi)型可以對(duì)應(yīng)多個(gè)類(lèi)型,只要他們都遵循統(tǒng)一協(xié)議。總得來(lái)說(shuō),協(xié)議類(lèi)型更具靈活性,底層類(lèi)型可以存儲(chǔ)更多的值,而不透明類(lèi)型對(duì)這些底層類(lèi)型有更強(qiáng)的限定性。用法如下:
    protocol Shape {
        func draw() -> String;
    }
    
    struct Quare: Shape {
        var size: Int
    
        func draw() -> String {
            return size.description;
        }
    }
    
    class ViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
        }
    
        /// 返回協(xié)議類(lèi)型
        func makeOneQuare() -> Shape {
            let q1 = Quare(size: 10)
            print(q1.draw())
            return q1;
        }
    
        /// 返回不透明類(lèi)型
        func makeSecondQuare() -> some Shape {
            let q2 = Quare(size: 10)
            print(q2.draw())
            return q2;
        }
    }
    
  2. 說(shuō)一下自己的看法吧:不透明類(lèi)型是可以被協(xié)議類(lèi)型替代使用的,這個(gè)就好像 weakunowned 兩個(gè)關(guān)鍵詞一樣,我們有知道區(qū)別的必要性,但是即使只用 weak,就可以解決循環(huán)引用的問(wèn)題了??赡苷娴挠兄荒苁褂?unowned 的情況,如果以后遇到,會(huì)在這里補(bǔ)充。

自動(dòng)引用計(jì)數(shù)

  1. 引用計(jì)數(shù)僅僅用于類(lèi)的實(shí)例。結(jié)構(gòu)體和枚舉都是值類(lèi)型,不是引用類(lèi)型,也不是通過(guò)引用的方式存儲(chǔ)和傳遞。
  2. 弱引用不會(huì)對(duì)其引用的實(shí)例保持強(qiáng)引用,因而不會(huì)阻止 ARC 銷(xiāo)毀被引用的實(shí)例。這個(gè)特性阻止了引用變?yōu)檠h(huán)引用。關(guān)鍵字 weak。另外,當(dāng) ARC 設(shè)置弱引用為 nil 時(shí),屬性觀察不會(huì)被觸發(fā)。
  3. 弱引用類(lèi)似,無(wú)主引用不會(huì)牢牢保持引用的實(shí)例。和弱引用不同的是,無(wú)主引用在其他實(shí)例有相同或更長(zhǎng)聲明周期時(shí)使用。關(guān)鍵字是 unowned 。無(wú)主引用通常都被期望有值。不過(guò) ARC 無(wú)法再實(shí)例被銷(xiāo)毀后將無(wú)主引用設(shè)為 nil ,因?yàn)榉强蛇x類(lèi)型的實(shí)例不允許被置為 nil。并且,使用無(wú)主引用,必須確保引用始終指向一個(gè)未被銷(xiāo)毀的實(shí)例。如果在該實(shí)例被銷(xiāo)毀后,訪問(wèn)該實(shí)例的無(wú)主引用,會(huì)觸發(fā)運(yùn)行時(shí)崩潰。
  4. 閉包和類(lèi)相似,都是引用類(lèi)型。循環(huán)引用可以通過(guò)弱引用或者無(wú)主引用來(lái)解決。

內(nèi)存安全

1. 內(nèi)存訪問(wèn)沖突的實(shí)質(zhì):
  1. 內(nèi)存訪問(wèn)沖突時(shí),要考慮內(nèi)存訪問(wèn)上下文的三個(gè)性質(zhì):訪問(wèn)是讀還是寫(xiě),訪問(wèn)的時(shí)長(zhǎng),以及訪問(wèn)的地址。特別是,沖突會(huì)發(fā)生在有兩個(gè)訪問(wèn)符合下列情況時(shí):
    • 至少有一個(gè)是寫(xiě)訪問(wèn)
    • 他們的訪問(wèn)是同一個(gè)內(nèi)存地址
    • 他們的訪問(wèn)在時(shí)間線上有部分的重疊。
2. Swift 中兩種長(zhǎng)期訪問(wèn)類(lèi)型:
  • In-Out 參數(shù)的訪問(wèn)沖突。
  • 結(jié)構(gòu)體 mutating 方法里。
3. 這個(gè)小節(jié)里舉的例子值得回頭再看看。

主要是針對(duì)長(zhǎng)期內(nèi)存訪問(wèn)的分析,和解決方法。

訪問(wèn)控制

1. 訪問(wèn)級(jí)別

Swift 為代碼中的實(shí)體提供了五種不同的訪問(wèn)級(jí)別:

  • openpublic 級(jí)別可以讓實(shí)體被同一模塊的所有實(shí)體訪問(wèn)。在模塊外也可以通過(guò)導(dǎo)入該模塊來(lái)訪問(wèn)原文件里的所有實(shí)體。通常情況下,使用 open 或者 public 來(lái)定義模塊對(duì)外訪問(wèn)接口。

    open 只能作用于類(lèi)或者類(lèi)的成員,它和 public 的區(qū)別主要在于 open 限定的類(lèi)或者類(lèi)的成員能夠在模塊外被繼承和重寫(xiě)。將類(lèi)的訪問(wèn)級(jí)別顯示指定為 open 表明已經(jīng)設(shè)計(jì)好了類(lèi)的代碼,并且充分考慮到了這個(gè)類(lèi)在其他模塊作為父類(lèi)時(shí)的影響。

  • internal 級(jí)別讓實(shí)體被同一模塊源文件的任何實(shí)體訪問(wèn),但是不能被模塊外的實(shí)體訪問(wèn)。通常情況下,某個(gè)實(shí)體只在應(yīng)用程序或者框架內(nèi)部使用,可以將其設(shè)置為 internal 級(jí)別。同時(shí),這個(gè)也是默認(rèn)訪問(wèn)級(jí)別。
  • fileprivate 限制實(shí)體只能在其定義的文件內(nèi)部訪問(wèn)。如果功能代碼的實(shí)現(xiàn)細(xì)節(jié)只需要在文件內(nèi)部訪問(wèn)時(shí),可以使用 fileprivate 來(lái)將其隱藏。
  • private 限制實(shí)體只能在其定義的作用域,以及同一文件的 extension 中訪問(wèn)。非同一個(gè)源文件的 extension ,也是無(wú)法訪問(wèn)的。

以上, open 的訪問(wèn)級(jí)別最高,限制最少。private 的訪問(wèn)級(jí)別最低,限制最多。

2. 訪問(wèn)級(jí)別規(guī)則

Swift 中訪問(wèn)級(jí)別遵循一個(gè)原則:實(shí)體不能定義在具有更低級(jí)別訪問(wèn)的實(shí)體中。例如:

  • 一個(gè) public 的變量,其所在類(lèi)型的訪問(wèn)級(jí)別不能是 internal、fileprivate 或者 private 。因?yàn)闊o(wú)法保證變量的類(lèi)型在使用變量的地方也具有訪問(wèn)權(quán)限。
  • 函數(shù)的訪問(wèn)級(jí)別不能高于它的參數(shù)類(lèi)型和返回類(lèi)型的訪問(wèn)級(jí)別。因?yàn)檫@樣就會(huì)出現(xiàn)函數(shù)可以在任何地方被訪問(wèn),而參數(shù)和返回類(lèi)型不可以的情況。

注意,即使違反上面的規(guī)則,正常情況下,也不會(huì)編譯或者運(yùn)行錯(cuò)誤…

高級(jí)運(yùn)算符

開(kāi)發(fā)中比較少需要使用高級(jí)運(yùn)算符,這里留著記錄,具體使用時(shí)在參考,點(diǎn)擊標(biāo)題即可。

1. 位運(yùn)算符
  • 按位取反運(yùn)算符 Bitwise NOT Operator

    • 按位取反運(yùn)算符 ~ 對(duì)一個(gè)數(shù)值的全部比特位進(jìn)行取反
    • 按位取反運(yùn)算符是一個(gè)前綴運(yùn)算符,直接放在運(yùn)算數(shù)之前,并且他們之間不能添加任何空格。
    let age = 18
    let invertAge = ~age
    print(invertAge)  // -19
    
  • 按位與運(yùn)算符 Bitwise AND Operator

    • 按位與運(yùn)算符 & 對(duì)兩個(gè)數(shù)的比特位進(jìn)行合并。它返回一個(gè)新的數(shù)。只有當(dāng)兩個(gè)數(shù)的全部對(duì)應(yīng)為都為 1 的時(shí)候,新數(shù)的對(duì)應(yīng)位才為 1 。
    let one = 10
    let sec = 10
    let third = 20
    let and1 = one & sec
    let and2 = one & third
    print(and1, and2)   // 10         0
    
  • 按位或運(yùn)算符 Bitwise OR Operaor

    • 按位或運(yùn)算符 | 可以對(duì)兩個(gè)數(shù)的比特位進(jìn)行比較。它返回一個(gè)新的數(shù),只要兩個(gè)數(shù)的對(duì)應(yīng)位中任意一個(gè)為 1 ,新數(shù)的對(duì)應(yīng)位就為 1 。
  • 按位異或運(yùn)算符 Bitwise XOR Operator

    • 按位異或運(yùn)算符,或稱(chēng)排外的運(yùn)算符 ^,可以對(duì)兩個(gè)數(shù)的比特位進(jìn)行比較。它返回一個(gè)新的數(shù),當(dāng)兩個(gè)數(shù)的對(duì)應(yīng)為不相同時(shí),新數(shù)的對(duì)應(yīng)位就為 1,對(duì)應(yīng)位相同時(shí),則為 0 。
  • 按位左移、按位右移運(yùn)算符 Bitwise Left Right Shift Operators

    • 按位左移運(yùn)算符 << 和按位右移運(yùn)算符 >> 可以對(duì)一個(gè)數(shù)的所有位指定位數(shù)的左移或者右移,但是需要遵循下面定義的規(guī)則。
    • 對(duì)一個(gè)數(shù)進(jìn)行按位左移或者按位右移,相當(dāng)于對(duì)這個(gè)數(shù)進(jìn)行乘以 2 或者除以 2 的運(yùn)算。將一個(gè)整數(shù)左移一位,等價(jià)于將這個(gè)數(shù)乘以 2 。同樣的,將一個(gè)整數(shù)右移一位,等價(jià)于將這個(gè)數(shù)除以 2 。
    • 無(wú)符號(hào)整數(shù)的移位運(yùn)算(簡(jiǎn)單)
    • 有符號(hào)整數(shù)的移位運(yùn)算(復(fù)雜)
2. 溢出運(yùn)算符
  • 溢出加法 &+
  • 溢出減法 &-
  • 溢出乘法 &*
3. 優(yōu)先級(jí)和結(jié)合性

運(yùn)算時(shí),加上必要的括號(hào),更具可讀性。

4. 運(yùn)算符函數(shù)

類(lèi)和結(jié)構(gòu)體可以為現(xiàn)有的運(yùn)算符提供自定義的實(shí)現(xiàn),這通常被稱(chēng)為 運(yùn)算符重載 。

  • 前綴和后綴運(yùn)算符,如 -a 、b!
  • 符合賦值運(yùn)算符,如 =+=
  • 等價(jià)運(yùn)算符,如 == 、 !=
5. 自定義運(yùn)算符

自定義運(yùn)算符的優(yōu)先級(jí)。

后記

本來(lái)是想一星期補(bǔ)完的, 結(jié)果斷斷續(xù)續(xù)持續(xù)了兩周多。這里僅僅是記錄自己日常中容易忽視的,或者不太常用的一些點(diǎn)。語(yǔ)言參考只是讓我們對(duì)這個(gè)語(yǔ)言有個(gè)大概的了解,想要加深對(duì) Swift 的理解,還是應(yīng)該更多的去看優(yōu)秀的源碼,自己動(dòng)手去實(shí)踐和總結(jié)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容