swift學習筆記4——擴充套件、協議

weixin_33860722發表於2016-06-21

之前學習swift時的個人筆記,根據github:the-swift-programming-language-in-chinese學習、總結,將重要的內容提取,加以理解後整理為學習筆記,方便以後查詢用。詳細可以參考the-swift-programming-language-in-chinese,或者蘋果官方英文版文件

當前版本是swift2.2

擴充套件(Extensions)

擴充套件 就是為一個已有的類、結構體、列舉型別或者協議型別新增新功能。這包括在沒有許可權獲取原始原始碼的情況下擴充套件型別的能力(即 逆向建模 )。擴充套件和 Objective-C 中的分類類似。(與 Objective-C 不同的是,Swift 的擴充套件沒有名字。)

如果你通過擴充套件為一個已有型別新增新功能,那麼新功能對該型別的所有已有例項都是可用的,即使它們是在這個擴充套件定義之前建立的。

計算型屬性(Computed Properties)

擴充套件可以為已有型別新增計算型例項屬性和計算型型別屬性。不可以新增儲存性屬性

構造器(Initializers)

擴充套件可以為已有型別新增新的構造器。這可以讓你擴充套件其它型別,將你自己的定製型別作為其構造器引數,或者提供該型別的原始實現中未提供的額外初始化選項。

擴充套件能為類新增新的便利構造器,但是它們不能為類新增新的指定構造器或析構器。指定構造器和析構器必須總是由原始的類實現來提供。

方法(Methods)

擴充套件可以為已有型別新增新的例項方法和型別方法。

可變例項方法(Mutating Instance Methods)

通過擴充套件新增的例項方法也可以修改該例項本身。結構體和列舉型別中修改 self 或其屬性的方法必須將該例項方法標註為 mutating,正如來自原始實現的可變方法一樣。

下面的例子為 Swift 的 Int 型別新增了一個名為 square 的可變方法,用於計算原始值的平方值:

extension Int {
    mutating func square() {
        self = self * self
    }
}

下標(Subscripts)

擴充套件可以為已有型別新增新下標。這個例子為 Swift 內建型別 Int 新增了一個整型下標。該下標 [n] 返回十進位制數字從右向左數的第 n 個數字:

123456789[0] 返回 9
123456789[1] 返回 8

巢狀型別(Nested Types)

擴充套件可以為已有的類、結構體和列舉新增新的巢狀型別:

協議

Mutating 方法要求

如果你在協議中定義了一個例項方法,該方法會改變採納該協議的型別的例項,那麼在定義協議時需要在方法前加 mutating 關鍵字。這使得結構體和列舉能夠採納此協議並滿足此方法要求。

將 mutating 關鍵字作為方法的字首,寫在 func 關鍵字之前,表示可以在該方法中修改它所屬的例項以及例項的任意屬性的值

實現協議中的 mutating 方法時,若是類型別,則不用寫 mutating 關鍵字。而對於結構體和列舉,則必須寫 mutating 關鍵字。

協議構造器要求

協議可以要求採納協議的型別實現指定的構造器。你可以像編寫普通構造器那樣,在協議的定義裡寫下構造器的宣告,但不需要寫花括號和構造器的實體:

protocol SomeProtocol {
    init(someParameter: Int)
}

構造器要求在類中的實現

你可以在採納協議的類中實現構造器,無論是作為指定構造器,還是作為便利構造器。無論哪種情況,你都必須為構造器實現標上 required 修飾符:

class SomeClass: SomeProtocol {
    required init(someParameter: Int) {
        // 這裡是構造器的實現部分
    }
}

使用 required 修飾符可以確保所有子類也必須提供此構造器實現,從而也能符合協議。

構造器要求
協議可以要求採納協議的型別實現指定的構造器。你可以像編寫普通構造器那樣,在協議的定義裡寫下構造器的宣告,但不需要寫花括號和構造器的實體:

protocol SomeProtocol {
init(someParameter: Int)
}
構造器要求在類中的實現

你可以在採納協議的類中實現構造器,無論是作為指定構造器,還是作為便利構造器。無論哪種情況,你都必須為構造器實現標上 required 修飾符:

class SomeClass: SomeProtocol {
    required init(someParameter: Int) {
        // 這裡是構造器的實現部分
    }
}

使用 required 修飾符可以確保所有子類也必須提供此構造器實現,從而也能符合協議。

如果一個子類重寫了父類的指定構造器,並且該構造器滿足了某個協議的要求,那麼該構造器的實現需要同時標註 required 和 override 修飾符:

protocol SomeProtocol {
    init()
}

class SomeSuperClass {
    init() {
        // 這裡是構造器的實現部分
    }
}

class SomeSubClass: SomeSuperClass, SomeProtocol {
    // 因為採納協議,需要加上 required
    // 因為繼承自父類,需要加上 override
    required override init() {
        // 這裡是構造器的實現部分
    }
}

如果類已經被標記為 final,那麼不需要在協議構造器的實現中使用 required 修飾符,因為 final 類不能有子類

協議作為型別

儘管協議本身並未實現任何功能,但是協議可以被當做一個成熟的型別來使用。

  • 作為函式、方法或構造器中的引數型別或返回值型別
  • 作為常量、變數或屬性的型別
  • 作為陣列、字典或其他容器中的元素型別

通過擴充套件采納協議

當一個型別已經符合了某個協議中的所有要求,卻還沒有宣告採納該協議時,可以通過空擴充套件體的擴充套件來採納該協議:

struct Hamster {
    var name: String
    var textualDescription: String {
        return "A hamster named \(name)"
    }
}
extension Hamster: TextRepresentable {}

從現在起,Hamster 的例項可以作為 TextRepresentable 型別使用:

let simonTheHamster = Hamster(name: "Simon")
let v1: TextRepresentable = simonTheHamster // 可以賦值
print(v1.textualDescription)

即使滿足了協議的所有要求,型別也不會自動採納協議,必須顯式地採納協議。

協議型別的集合

協議型別可以在陣列或者字典這樣的集合中使用,在協議型別提到了這樣的用法。下面的例子建立了一個元素型別為 TextRepresentable 的陣列:

let things: [TextRepresentable] = [game, d12, simonTheHamster]
如下所示,可以遍歷 things 陣列,並列印每個元素的文字表示:

for thing in things {
    print(thing.textualDescription)
}
// A game of Snakes and Ladders with 25 squares
// A 12-sided dice
// A hamster named Simon

thing 是 TextRepresentable 型別而不是 Dice,DiceGame,Hamster 等型別,即使例項在幕後確實是這些型別中的一種。由於 thing 是 TextRepresentable 型別,任何 TextRepresentable 的例項都有一個 textualDescription 屬性,所以在每次迴圈中可以安全地訪問 thing.textualDescription。

協議的繼承

協議能夠繼承一個或多個其他協議,可以在繼承的協議的基礎上增加新的要求。協議的繼承語法與類的繼承相似,多個被繼承的協議間用逗號分隔:

protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
    // 這裡是協議的定義部分
}

類型別專屬協議

你可以在協議的繼承列表中,通過新增 class 關鍵字來限制協議只能被類型別採納,而結構體或列舉不能採納該協議。class 關鍵字必須第一個出現在協議的繼承列表中,在其他繼承的協議之前:

protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
    // 這裡是類型別專屬協議的定義部分
}

在以上例子中,協議 SomeClassOnlyProtocol 只能被類型別採納。如果嘗試讓結構體或列舉型別採納該協議,則會導致編譯錯誤。

協議合成

有時候需要同時採納多個協議,你可以將多個協議採用 protocol<SomeProtocol, AnotherProtocol> 這樣的格式進行組合,稱為 協議合成(protocol composition)。你可以在 <> 中羅列任意多個你想要採納的協議,以逗號分隔。

下面的例子中,將 Named 和 Aged 兩個協議按照上述語法組合成一個協議,作為函式引數的型別:

protocol Named {
    var name: String { get }
}
protocol Aged {
    var age: Int { get }
}
struct Person: Named, Aged {
    var name: String
    var age: Int
}
func wishHappyBirthday(celebrator: protocol<Named, Aged>) {
    print("Happy birthday \(celebrator.name) - you're \(celebrator.age)!")
}

協議合成並不會生成新的、永久的協議型別,而是將多個協議中的要求合成到一個只在區域性作用域有效的臨時協議中。

可選的協議要求

協議可以定義可選要求,採納協議的型別可以選擇是否實現這些要求。在協議中使用 optional 關鍵字作為字首來定義可選要求。使用可選要求時(例如,可選的方法或者屬性),它們的型別會自動變成可選的。比如,一個型別為 (Int) -> String 的方法會變成 ((Int) -> String)?。需要注意的是整個函式型別是可選的,而不是函式的返回值。

相關文章