1. 程式人生 > 其它 >Swift文件Chapter 21 協議

Swift文件Chapter 21 協議

協議規定了一個藍圖,規定了用來實現某一特定任務或者功能的方法、屬性,以及其他需要的東西。

協議語法

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

自定義型別如果需要遵循協議,需要用:分隔,遵循多個協議使用,分隔。

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

如果這個型別擁有一個父類,那麼父類名放在協議名之前:

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

屬性要求

協議可以要求提供特定名稱的例項屬性或者型別屬性,不指定是計算屬性還是儲存屬性。同時,也可以定義屬性是可讀的還是可讀可寫的。
如果協議只要求可讀,實現中可以是可讀可寫的。協議用var來宣告變數屬性,在宣告型別後加上{set get}表示屬性可讀可寫,加上{get}表明屬性可讀。
使用static字首可以宣告型別屬性,在類例項實現時可以使用class關鍵字。

方法要求

協議中方法的定義和其他型別中完全相同使用func關鍵字,只不過沒有花括號的函式體。在協議中定義類方法的時候,總是使用static關鍵字作為字首。即使在類實現時,類方法要求使用classstatic作為關鍵字字首。

異變方法要求

有時需要在方法中改變(或異變)方法所屬的例項。例如,在值型別(即結構體和列舉)的例項方法中,將mutating關鍵字作為方法的字首,寫在func關鍵字之前,表示可以在該方法中修改它所屬的例項以及例項的任意屬性的值。如果是類型別實現mutating函式可以不加關鍵字,但是列舉型別和結構體型別需要。

構造器要求

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

protocol SomeProtocol {
    init(someParameter: Int)
}

協議構造器要求的類實現

類實現協議的構造器,無論是指定構造器,還是作為便利構造器,都需要加上required

修飾符。
required修飾符可以確保所有子類也必須提供此構造器實現,如果是final類,那麼不需要required修飾符。
如果一個子類重寫了父類的指定構造器,並且該構造器滿足了某個協議的要求,那麼該構造器的實現需要同時標註requiredoverride修飾符。

可失敗構造器要求

協議還可以為遵循協議的型別定義可失敗構造器要求。遵循協議的型別可以通過可失敗構造器(init?)或非可失敗構造器(init)來滿足協議中定義的可失敗構造器要求。

協議作為型別

協議本身並未實現任何功能,但是協議可以作為一個型別來使用。協議作為型別使用通常被稱為存在型別
協議可以像普通型別一樣使用:

  • 作為函式,方法以及構造器裡的引數型別或者返回值型別;
  • 作為常量,變數或者屬性的型別;
  • 作為陣列,字典等其他容器的元素型別。

協議是一種型別,因此命名也要使用大寫字母開頭的駝峰命名法。

當我們在型別中定義協議引數的時候,我們只需要傳入一個遵循了該協議的型別即可。我們只能使用協議內定義的方法和型別,不可以使用底層的型別提供的方法和屬性。

委託

委託是一種設計模式,允許類或者結構體把一部分功能委託給其他型別的例項。委託模式需要定義協議封裝需要實現的功能,能確保遵循協議的型別能提供這些功能。

在擴充套件裡新增協議遵循

在無法修改原始碼的情況下,我們可以擴充套件令已有型別遵循並符合協議。通過擴充套件可以給型別增加新的協議並使其遵循這個協議。和原始協議中定義並遵循協議是一樣的。需要使用extension關鍵字。

有條件地遵循協議

如果我們在泛型中使用擴充套件新增協議,可能只符合某一種型別。因此我們使用where擴充套件型別時列出限制讓泛型型別有條件地遵循某協議。

在擴充套件裡宣告採納協議

如果說某個型別已經實現了協議,但是並沒有遵循該協議。那麼使用空的擴充套件讓它來遵循協議。

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

即使型別已經滿足了協議要求,也不會自動去遵循協議。必須顯式地遵循協議。

協議型別的集合

協議型別可以在陣列或者字典這樣的集合中使用。

協議的繼承

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

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

類的專屬協議

新增AnyObject到協議的繼承列表,那麼這個協議只可以用在類型別,不能用在結構體或者列舉型別。

協議合成

如果說一個型別需要遵循多個協議,那麼可以使用協議合成複合成同一個協議,使用SomeProtocol & AnotherProtocol,同時協議組合也能包含類型別,這允許你標明一個需要的父類。

protocol Named {
    var name: String { get }
}
protocol Aged {
    var age: Int { get }
}
struct Person: Named, Aged {
    var name: String
    var age: Int
}
func wishHappyBirthday(to celebrator: Named & Aged) {
    print("Happy birthday, \(celebrator.name), you're \(celebrator.age)!")
}
let birthdayPerson = Person(name: "Malcolm", age: 21)
wishHappyBirthday(to: birthdayPerson)
// 列印 “Happy birthday Malcolm - you're 21!”

檢查協議一致性

可以使用is或者as進行協議型別檢查,檢查是否符合某個協議。

  • is檢查例項是否符合某個協議,符合返回true,否則返回false
  • as?返回一個可選值,當例項符合某個協議時,返回型別為協議型別的可選值,否則返回nil
  • as!將例項強制向下轉換到某個協議型別,如果強轉失敗,將觸發執行時錯誤。

可選的協議要求

協議可以定義可選的要求,遵循協議的型別可以選擇是否實現這些要求。在協議中可選的要求使用optional關鍵字作為字首。可選要求如果用在和Object-C打交道的程式碼中,協議和可選要求都必須加上@obj屬性。記@objc特性的協議只能被繼承自Objective-C類的類或者@objc類遵循,其他類以及結構體和列舉均不能遵循這種協議。
可選要求的屬性可會變成可選的,即使是函式也會變成可選型別,而不是返回值可選。協議中的可選要求可通過可選鏈式呼叫來使用,因為遵循協議的型別可能沒有實現這些可選要求。

協議擴充套件

協議可以通過擴充套件來為遵循協議的型別提供屬性、方法以及下標的實現。這樣就不需要在所有遵循該協議的型別進行重複的實現,也不用使用全域性函式。

提供預設實現

可以通過協議擴充套件來為協議要求的屬性、方法以及下標提供預設的實現。如果遵循協議的型別為這些要求提供了自己的實現,那麼這些自定義實現將會替代擴充套件中的預設實現被使用

為協議擴充套件新增限制條件

在擴充套件協議的時候,可以指定一些限制條件,只有遵循協議的型別滿足這些限制條件時,才能獲得協議擴充套件提供的預設實現。這些限制條件寫在協議名之後,使用where子句來描述。