Swift中協(xié)議的簡單介紹

前言

熟悉Objective-C語言的同學(xué)們肯定對協(xié)議都不陌生,在Swift中蘋果將protocol這種語法發(fā)揚(yáng)的更加深入和徹底揩徊。Swift中的protocol不僅能定義方法還能定義屬性腰鬼,配合extension擴(kuò)展的使用還能提供一些方法的默認(rèn)實(shí)現(xiàn),而且不僅類可以遵循協(xié)議塑荒,現(xiàn)在的枚舉和結(jié)構(gòu)體也能遵循協(xié)議了熄赡。基于此本文從** 1齿税、協(xié)議中定義屬性和方法殖告,2秘蛔、協(xié)議的繼承荞下、聚合、關(guān)聯(lián)類型词渤,3、協(xié)議的擴(kuò)展串绩,4缺虐、Swift標(biāo)準(zhǔn)庫中常見的協(xié)議5赏参、為什么要使用協(xié)議** 5個方面結(jié)合自身的學(xué)習(xí)經(jīng)驗(yàn)簡單介紹一下這種“加強(qiáng)型protocol的使用志笼,入門級沿盅、屬于學(xué)習(xí)總結(jié)把篓,希望能給正在學(xué)習(xí)Swift的小伙伴們一點(diǎn)啟發(fā)。

協(xié)議中定義屬性和方法

  • 協(xié)議的定義
    官方文檔對協(xié)議的定義是這樣的:

協(xié)議為方法腰涧、屬性韧掩、以及其他特定的任務(wù)需求或功能定義藍(lán)圖。協(xié)議可被類窖铡、結(jié)構(gòu)體疗锐、或枚舉類型采納以提供所需功能的具體實(shí)現(xiàn)。滿足了協(xié)議中需求的任意類型都叫做遵循了該協(xié)議费彼。

Swift中定義一個協(xié)議和定義枚舉滑臊、結(jié)構(gòu)體或者類的格式類似,使用protocol關(guān)鍵字:

//定義一個名字為學(xué)生協(xié)議
protocol Student {
}

這里Student是使用**protocol **關(guān)鍵字聲明的一個協(xié)議箍铲,和枚舉雇卷、結(jié)構(gòu)體、類命名原則相似颠猴,Student首字母大寫表示在以后的使用中很可能會將Student看作是一個類型使用关划。

  • 協(xié)議中定義屬性
    協(xié)議中定義屬性表示遵循該協(xié)議的類型具備了某種屬性,具體來說只能使用var關(guān)鍵字聲明并且必須明確規(guī)定該屬性是可讀的get翘瓮、還是可讀可寫的get set贮折,另外還可以通過關(guān)鍵字static聲明一個類型屬性。示例如下:
protocol Student {
    //定義一個可讀可寫的 name 屬性
    var name: String { get set }
    //定義一個可讀的 birthPlace 屬性
    var birthPlace: String { get }
    //定義一個類屬性 record
    static var qualification: String {get}
}

和定義方法一樣资盅,我們只需要確定該屬性具體是什么類型并且添加對應(yīng)的關(guān)鍵字调榄,不需要具體的實(shí)現(xiàn),更不能為他們賦上初始值(類似于計算屬性)呵扛。定義好屬性之后振峻,我們就可以利用屬性來做點(diǎn)事情了。

struct Puple: Student {
    static var qualification: String = "小學(xué)"
    var name: String
    var birthPlace: String = "北京"
}
var p1 = Puple(name: "小明", birthPlace: "上海")

定義一個Puple結(jié)構(gòu)體遵循Student協(xié)議择份,該結(jié)構(gòu)體中必須存在協(xié)議要求聲明的三個屬性matrikelnummer扣孟、namebirthPlace,static修飾的類型屬性必須被有初始值或者存在get荣赶、set方法凤价。對于普通的實(shí)例屬性協(xié)議并不關(guān)心是計算型屬性還是存儲型屬性鸽斟。實(shí)例中的屬性同樣可以被修改:

var p1 = Puple(name: "小明", birthPlace: "上海")
Puple.qualification = "中學(xué)"

看到這里有的同學(xué)可能有些疑問,birthPlace利诺、qualification明明只有get方法為什么卻可以修改賦值呢富蓄?其實(shí)協(xié)議中的“只讀”屬性修飾的是協(xié)議這種“類型”的實(shí)例,例如下面的例子:

var s1: Student = p1
s1.birthPlace = "廣州"

雖然我們并不能像創(chuàng)建類的實(shí)例那樣直接創(chuàng)建協(xié)議的實(shí)例慢逾,但是我們可以通過“賦值”得到一個協(xié)議的實(shí)例立倍。將p1的值賦值給Student類型的變量s1,修改s1birthPlace屬性時編譯器就會報錯:birthPlace是一個只讀的屬性,不能被修改侣滩。如果Puple中還存在Student沒有的屬性口注,那么在賦值過程中s1將不會存在這樣的屬性,盡管這樣做的意義并不大君珠,但是我們從中知道了協(xié)議中get寝志、set的具體含義。

  • 協(xié)議中定義方法
    和Objective-C類似策添,Swift中的協(xié)議可以定義類型方法或者實(shí)例方法材部,方法的參數(shù)不能有默認(rèn)值(Swift認(rèn)為默認(rèn)值也是一種變相的實(shí)現(xiàn)),在遵守該協(xié)議的類型中具體實(shí)現(xiàn)方法的細(xì)節(jié)唯竹,通過類或?qū)嵗{(diào)用:
protocol Student {
    //類方法
    static func study()
    //實(shí)例方法
    func changeName()
}
struct CollageStudent: Student {
    //類方法實(shí)現(xiàn)
    static func study() {
    }
    //實(shí)例方法實(shí)現(xiàn)
    func changeName() {
    }
}
//方法的調(diào)用
CollageStudent.study()
var c1 = CollageStudent()
c1.changeName()

注意:當(dāng)我們在結(jié)構(gòu)體中的方法修改到屬性的時候需要在方法前面加上關(guān)鍵字mutating表示該屬性能夠被修改(如果是類不需要添加mutating 關(guān)鍵字),這樣的方法叫做:異變方法乐导,和 “在實(shí)例方法中修改值類型” 的處理是一樣的。

protocol Student {
    mutating func changeName()
}
struct CollageStudent: Student {
    mutating func changeName() {
        self.name = "小明"
    }
}
var c1 = CollageStudent()
c1.changeName()
  • 協(xié)議中的初始化器
    我們可以在協(xié)議中定義遵循協(xié)議的類型需要實(shí)現(xiàn)的指定初始化器(構(gòu)造函數(shù))或者便捷初始化器浸颓。
protocol Pet {
    init(name: String)
}
class Cat: Pet {
    var name: String = "Cat"
    required init(name: String) {
        self.name = name
    }
}

Cat由于遵循了Pet協(xié)議物臂,應(yīng)該用required關(guān)鍵字修飾初始化器的具體實(shí)現(xiàn)。
如果一個類既繼承了某個類猾愿,而且遵循了一個或多個協(xié)議鹦聪,我們應(yīng)該將父類放在最前面,然后依次用逗號排列蒂秘。

class SomeClass: OneProtocol, TwoProtocol {
}

這是因?yàn)镾wift中類的繼承是單一的泽本,但是類可以遵守多個協(xié)議,因此為了突出其單一父類的特殊性姻僧,我們應(yīng)該將繼承的父類放在最前面规丽,將遵守的協(xié)議依次放在后面。

  • 多個協(xié)議重名方法調(diào)用沖突
    由于在Swift中并沒有規(guī)定不同的協(xié)議內(nèi)方法不能重名(這樣的規(guī)定也是不合理的)撇贺。因此我們在自定義多個協(xié)議中方法重名的情況是可能出現(xiàn)的赌莺,比如存在TextOneTextTwo兩個協(xié)議松嘶,定義如下:
protocol TextOne {
    func text() -> Int
}
protocol TextTwo {  
    func text() -> String
}

這兩個協(xié)議中的text()方法名相同返回值不同艘狭,如果存在一個類型Person同時遵守了TextOneTextTwo,在Person實(shí)例調(diào)用方法的時候就會出現(xiàn)歧義。

struct Person: TextOne, TextTwo {
    func text() -> Int {
        return 10
    }
    func text() -> String { 
        return "hello"
    }
}
let p1 = Person()
//嘗試調(diào)用返回值為Int的方法
let num = p1.text()
//嘗試調(diào)用返回值為String的方法
let string = p1.text()

上面的調(diào)用肯定是無法通過的巢音,因?yàn)榫幾g器無法知道同名text()方法到底是哪個協(xié)議中的方法遵倦,那么出現(xiàn)這種情況的根本原因在于調(diào)用哪個協(xié)議的text()不確定,因此我們需要指定調(diào)用特定協(xié)議的text()方法官撼,改進(jìn)后的代碼如下:

//嘗試調(diào)用返回值為Int的方法
let num = (p1 as TextOne).text()
//嘗試調(diào)用返回值為String的方法
let string = (p1 as TextTwo).text()

也可以理解為在進(jìn)行調(diào)用前將p1常量進(jìn)行類型轉(zhuǎn)換梧躺。

協(xié)議的繼承、聚合傲绣、關(guān)聯(lián)類型

  • 協(xié)議的繼承
    官方文檔說明:

協(xié)議可以繼承一個或者多個其他協(xié)議并且可以在它繼承的基礎(chǔ)之上添加更多要求掠哥。協(xié)議繼承的語法與類繼承的語法相似,選擇列出多個繼承的協(xié)議秃诵,使用逗號分隔续搀。

protocol OneProtocol {  
}
protocol TwoProtocol {
}
//定義一個繼承子OneProtocol 和 TwoProtocol協(xié)議的新的協(xié)議: ThreeProtocol
protocol ThreeProtocol: OneProtocol, TwoProtocol {
}

如上所示,任何遵守了ThreeProtocol協(xié)議的類型都應(yīng)該同時實(shí)現(xiàn)OneProtocolTwoProtocol的要求必須實(shí)現(xiàn)的方法或?qū)傩浴?/p>

  • 協(xié)議的聚合
    日常開發(fā)中要求一個類型同時遵守多個協(xié)議是很常見的顷链,除了使用協(xié)議的繼承外我們還可以使用形如OneProtocol & TwoProtocol的形式實(shí)現(xiàn)協(xié)議聚合(組合)復(fù)合多個協(xié)議到一個要求里目代。例如:
//協(xié)議聚合成臨時的類型
typealias Three = TwoProtocol & OneProtocol
//協(xié)議聚合成為參數(shù)的類型
func text(paramter: OneProtocol & TwoProtocol) {
}

一個很常見的例子:定義text函數(shù)的參數(shù)類型使用了協(xié)議的聚合屈梁,在這里我們并不關(guān)心paramter是什么類型的參數(shù)嗤练,只要它遵循這兩個要求的協(xié)議即可。

  • 繼承和聚合在使用上的區(qū)別
    善于思考的同學(xué)可以發(fā)現(xiàn)在讶,要實(shí)現(xiàn)上面的 "paramter參數(shù)的類型是遵守OneProtocol 和 TwoProtoco" 的效果煞抬,完全可以使用協(xié)議的繼承,新定義一個協(xié)議ThreeProtocol繼承自OneProtocolTwoProtocol构哺,然后指定paramter參數(shù)的類型是ThreeProtocol類型革答。那么這兩種方法有何區(qū)別呢?
    首先協(xié)議的繼承是定義了一個全新的協(xié)議曙强,我們是希望它能夠“大展拳腳”得到普遍使用残拐。而協(xié)議的聚合不一樣,它并沒有定義新的固定協(xié)議類型碟嘴,相反溪食,它只是定義一個臨時的擁有所有聚合中協(xié)議要求組成的局部協(xié)議,很可能是“一次性需求”娜扇,使用協(xié)議的聚合保持了代碼的簡潔性错沃、易讀性,同時去除了定義不必要的新類型的繁瑣雀瓢,并且定義和使用的地方如此接近枢析,見明知意,也被稱為匿名協(xié)議聚合刃麸。但是使用了匿名協(xié)議聚合能夠表達(dá)的信息就少了一些醒叁,所以需要開發(fā)者斟酌使用。

  • 協(xié)議的檢查
    如何檢查某個類型是否遵循了特定的協(xié)議?:使用關(guān)鍵字 is把沼,同時該運(yùn)算符會返回一個Bool值用于判斷條件是否成立断傲。

struct Person: OneProtocol {
}
let p1 = Person()
if (p1 is OneProtocol){ //可以理解為:p1 是一個遵守了OneProtocol協(xié)議類型的實(shí)例
    print("yes")
}

如何讓定義的協(xié)議只能被類遵守?:使用關(guān)鍵字class智政,該關(guān)鍵字修飾之后表示協(xié)議只能被類遵守认罩,如果有枚舉或結(jié)構(gòu)體嘗試遵守會報錯。

//只能被類遵守的協(xié)議
protocol FourProtocol: class ,ThreeProtocol {
}
//此處報錯
struct Person: FourProtocol {
}
class Perple: FourProtocol {
}
  • 關(guān)聯(lián)類型
    協(xié)議的關(guān)聯(lián)類型指的是根據(jù)使用場景的變化续捂,如果協(xié)議中某些屬性存在“邏輯相同的而類型不同”的情況垦垂,可以使用關(guān)鍵字associatedtype來為這些屬性的類型聲明“關(guān)聯(lián)類型”。
protocol WeightCalculable {
    //為weight 屬性定義的類型別名
    associatedtype WeightType
    var weight: WeightType { get }
}

WeightCalculable是一個“可稱重”協(xié)議牙瓢,weight屬性返回遵守該協(xié)議具體類型的實(shí)例的重量劫拗。這里我們使用associatedtype為該屬性的類型定義了一個別名WeightType,換言之在WeightCalculable中并不關(guān)心weight的類型是Int 還是Double或者是其他類型,他只是簡單的告訴我們返回的類型是WeightType矾克,至于WeightType到底是什么類型由遵守該協(xié)議的類中自己去定義页慷。那么這樣做的好處是什么呢?

//定義手機(jī)結(jié)構(gòu)體
struct MobilePhone: WeightCalculable {
    typealias WeightType = Double
    var weight: WeightType
}
let iPhone7 = MobilePhone(weight: 0.138)
//定義汽車結(jié)構(gòu)體
struct Car: WeightCalculable {
    typealias WeightType = Int
    var weight: WeightType
}
let truck = Car(weight: 3000_000)

如上所示:MobilePhone胁附、Car類型都遵守了WeightCalculable協(xié)議酒繁,都能被稱重,但是手機(jī)由于結(jié)構(gòu)精密控妻、體型小巧州袒,小數(shù)點(diǎn)后面的數(shù)字對于稱重來說是必不可少的,所以使用了Double類型弓候,返回0.138千克138克郎哭,但是對于汽車這樣的龐然大物在稱重時如果還計較小數(shù)點(diǎn)后面的數(shù)字就顯得沒有意義了,所以使用Int類型菇存,表示3000千克也就是3噸夸研。
從上面的例子可以很好的看出由于MobilePhoneCar稱重時邏輯是一樣的依鸥,但是對于weight屬性的返回值要求不一樣亥至,如果僅僅因?yàn)榉祷刂殿愋偷牟煌x兩個類似的協(xié)議一個是Int類型另外一個是Double類型,這樣做顯然是重復(fù)的毕籽、不合適的抬闯。所以associatedtype在這種情況下就發(fā)揮出作用了,他讓開發(fā)者在遵守協(xié)議時根據(jù)需求去定義返回值的類型关筒,而不是在協(xié)議中寫死溶握。唯一要注意的是:一定要在遵守該協(xié)議的類型中使用typealias規(guī)定具體的類型。不然編譯器就報錯了蒸播。

協(xié)議的擴(kuò)展

協(xié)議的擴(kuò)展是協(xié)議中很重要的一部分內(nèi)容睡榆,主要體現(xiàn)在以下兩個方面:

  • 擴(kuò)展協(xié)議的屬性和方法
    我們通過一個常見的例子說明一下:
protocol Score {
    var math: Int { get set}
    var english: Int {get set}
    func mathPercent() -> Double
}

首先定義一個Score協(xié)議萍肆,里面有兩個Int類型的屬性mathenglish和一個計算數(shù)學(xué)所占分?jǐn)?shù)的比例的方法mathPercent胀屿。

struct Puple: Score {
    var math: Int
    var english: Int
    func mathPercent() -> Double {
        return Double(math) / Double(math + english)
    }
}

定義Puple遵守該協(xié)議塘揣,實(shí)現(xiàn)了必要的屬性和方法。

let p1 = Puple(math: 90, english: 80)
s1.mathPercent()

通過上面的代碼可以計算出s1中數(shù)學(xué)所占的比例宿崭,但是設(shè)想一下如果還有很多個類似Puple結(jié)構(gòu)體的類型都需要遵守該協(xié)議亲铡,都需要默認(rèn)實(shí)現(xiàn)mathPercent 方法計算出自己的數(shù)學(xué)分?jǐn)?shù)所占的比例,還是按照上面的寫法代碼量就很大而且很冗雜了葡兑。問題的關(guān)鍵在于:任何遵守Score協(xié)議類型的mathPercent計算邏輯是不變的奖蔓,而且需要默認(rèn)實(shí)現(xiàn)。那么我們?nèi)绾屋p松的實(shí)現(xiàn)這樣的效果呢讹堤,答案是:為Score添加方法的擴(kuò)展吆鹤。

extension Score {
    func mathPercent() -> Double {
        return Double(math) / Double(math + english)
    }
}

mathPercent的具體實(shí)現(xiàn)寫在協(xié)議的擴(kuò)展里面,就能為所有的遵守Score的類型提供mathPercent默認(rèn)的實(shí)現(xiàn)洲守。

struct CollageStudent: Score {
    var math: Int
    var english: Int
}
let c1 = CollageStudent(math: 80, english: 80)
c1.mathPercent()

如此就能起到“不實(shí)現(xiàn)mathPercent方法也能計算出數(shù)學(xué)所占分?jǐn)?shù)的比例”的效果了疑务。此語法在Swift中有一個專業(yè)術(shù)語叫做:default implementation 即默認(rèn)實(shí)現(xiàn)。包括計算屬性和方法的默認(rèn)實(shí)現(xiàn)梗醇,但是不支持存儲屬性知允,如果遵循類型給這個協(xié)議的要求提供了它自己的實(shí)現(xiàn),那么它就會替代擴(kuò)展中提供的默認(rèn)實(shí)現(xiàn)婴削。
通過這樣的語法廊镜,我們不僅能為自定義的協(xié)議提供擴(kuò)展牙肝,還能為系統(tǒng)提供的協(xié)議添加擴(kuò)展唉俗,例如,為CustomStringConvertible添加一個計算屬性默認(rèn)實(shí)現(xiàn)的擴(kuò)展:

extension CustomStringConvertible {
    var customDescription: String {
        return "YQ" + description
    }
}
  • 為存在的類型添加協(xié)議遵守
    官方文檔說明:

擴(kuò)展一個已經(jīng)存在的類型來采納和遵循一個新的協(xié)議配椭,無需訪問現(xiàn)有類型的源代碼虫溜。擴(kuò)展可以添加新的屬性、方法和下標(biāo)到已經(jīng)存在的類型股缸,并且因此允許你添加協(xié)議需要的任何需要衡楞。

簡單的來說我們可以對存在的類型(尤其是系統(tǒng)的類型)添加協(xié)議遵守。盡管這更像是對“類型的擴(kuò)展”敦姻,但是官方文檔將這部分放在了協(xié)議的擴(kuò)展中瘾境。

extension Double : CustomStringConvertible {
    /// A textual representation of the value.
    public var description: String { get }
}

上面的代碼就是Swift標(biāo)準(zhǔn)庫中對于Double類型添加的一個協(xié)議遵守。除了添加系統(tǒng)協(xié)議的遵守镰惦,我們還可以添加自定義的協(xié)議的遵守迷守,其方法都是一樣的,這里就不太贅述了旺入。

  • 總結(jié)
    通過協(xié)議的擴(kuò)展提供協(xié)議中某些屬性和方法的默認(rèn)實(shí)現(xiàn)兑凿,將公共的代碼和屬性統(tǒng)一起來極大的增加了代碼的復(fù)用凯力,同時也增加了協(xié)議的靈活性和使用范圍,這樣的協(xié)議不僅僅是一系列接口的規(guī)范礼华,還能提供相應(yīng)的邏輯咐鹤,是面向協(xié)議編程的基礎(chǔ)。

Swift標(biāo)準(zhǔn)庫中常見的協(xié)議

學(xué)習(xí)完協(xié)議的基礎(chǔ)語法圣絮,我們大致熟悉一下Swift標(biāo)準(zhǔn)庫中提供的協(xié)議祈惶。


55個標(biāo)準(zhǔn)庫協(xié)議

Swift標(biāo)準(zhǔn)庫為我們提供了55種協(xié)議,他們的命名很有特點(diǎn)扮匠,基本是以"Type"行瑞、“able”、“Convertible” 結(jié)尾餐禁,分別表示該協(xié)議“可以被當(dāng)做XX類型”血久、“具備某種能力或特性”、“能夠進(jìn)行改變或變換”帮非。因此在自定義協(xié)議時應(yīng)該盡可能遵守蘋果的命名規(guī)則氧吐,便于開發(fā)人員之間的高效合作。下面介紹一下常見的幾種協(xié)議:

  • Equatable
    Equatable是和比較相關(guān)的協(xié)議末盔,遵守該協(xié)議表示實(shí)例能夠用于相等的比較筑舅,需要重載==運(yùn)算符。
struct Student: Equatable {
    var math: Int
    var english: Int
}
//重載 == 運(yùn)算符
func == (s1: Student, s2: Student) -> Bool {
    return s1.math == s2.math && s1.english == s2.english
}

Student遵守Equatable并且重載了==運(yùn)算符后就能直接比較兩個學(xué)生的成績是否相等了陨舱。

let s1 = Student(math: 80, english: 60)
let s2 = Student(math: 70, english: 90)
s1 == s2 //false

值得注意的是翠拣,由于重載==運(yùn)算符是遵守Equatable協(xié)議后要求我們實(shí)現(xiàn)的,因此重載方法應(yīng)該緊跟在遵守該協(xié)議的類型定義后游盲,中間不能有其他的代碼误墓,否則就報錯了。

  • Comparable
    Comparable是和比較相關(guān)的第二個協(xié)議益缎,遵守該協(xié)議表示實(shí)例能夠進(jìn)行比較谜慌,需要重載<運(yùn)算符。
struct Student: Comparable{
    var math: Int
    var english: Int
}
//重載 < 運(yùn)算符
func < (s1: Student, s2: Student) -> Bool {
    return (s1.math + s1.english) < (s2.math + s2.english)
}
let s1 = Student(math: 80, english: 60)
let s2 = Student(math: 70, english: 90)
s1 < s2 //true
  • CustomStringConvertible
    CustomStringConvertible提供了一種用文本表示一個對象或者結(jié)構(gòu)的方式,可以在任何遵守該協(xié)議的類型中自定義表示結(jié)構(gòu)的文本莺奔,需要覆蓋description屬性欣范。
struct Student: CustomStringConvertible{
    var math: Int
    var english: Int
    var description: String {
        return "Your math:" + String(math) + ", english:" + String(english)
    }
}
let s1 = Student(math: 80, english: 60)
print(s1) // Your math:80, english:60
  • ExpressibleByArrayLiteral
    ExpressibleByArrayLiteral提供了使用數(shù)組文本初始化的類型的能力,具體來說使用逗號分隔的值令哟、實(shí)例恼琼、字面值列表,方括號以創(chuàng)建數(shù)組文本屏富。遵守該協(xié)議需要實(shí)現(xiàn)init(arrayLiteral elements: Person.Element...)方法晴竞。
struct Person: ExpressibleByArrayLiteral {
    var name: String = ""
    var job: String = ""
    typealias Element = String
    init(arrayLiteral elements: Person.Element...) {  
        if elements.count == 2 {
            name = elements[0]
            job = elements[1]
        }
    }
}
let p1: Person = ["jack", "teacher"]
print(p1.name) //jack
print(p1.job) //teacher

上面的代碼用到了之前關(guān)聯(lián)類型,通過遵守ExpressibleByArrayLiteral役听,現(xiàn)在的Person就可以使用數(shù)組直接創(chuàng)建實(shí)例了颓鲜。

類似的協(xié)議還有:
ExpressibleByDictionaryLiteral表窘、ExpressibleByStringLiteralExpressibleByBooleanLiteral 甜滨、ExpressibleByIntegerLiteral等等乐严,相信大家通過名稱就能大概猜出具體作用,由于篇幅有限這里就不再贅述了衣摩。

為什么要使用協(xié)議

  • 協(xié)議可以作為類型使用
    協(xié)議作為一種類型是蘋果在Swift中提出的昂验,并且在官方文檔中還為我們具體指出了可以將協(xié)議當(dāng)做類型使用的場景:

1,在函數(shù)艾扮、方法或者初始化器里作為形式參數(shù)類型或者返回類型既琴;

2,作為常量泡嘴、變量或者屬性的類型甫恩;
3,作為數(shù)組酌予、字典或者其他存儲器的元素的類型磺箕。

  • 協(xié)議可以解決面向?qū)ο笾幸恍┘值膯栴}

    寵物類圖

    如圖所示的類結(jié)構(gòu)圖中麻雀在寵物類圖中的位置顯得比較尷尬,之所以尷尬是因?yàn)?code>麻雀作為一種鳥類抛虫,應(yīng)該繼承松靡,但是如果繼承了,就相當(dāng)于默認(rèn)了麻雀是一種寵物建椰,這顯然是不和邏輯的雕欺。解決此問題的一般方法如下:
    寵物類圖

    乍一看好像解決了這樣的問題,但是仔細(xì)想由于Swift只支持單繼承棉姐,麻雀沒有繼承類就無法體現(xiàn)麻雀作為一種鳥擁有的特性和方法(比如飛翔)屠列,如果此時出現(xiàn)一個新的飛機(jī)類,雖然飛機(jī)寵物之間沒有任何聯(lián)系谅海,但是飛機(jī)是由很多共同特性的(比如飛翔)脸哀,這樣的特性該如何體現(xiàn)呢?答案還是新建一個類成為動物飛機(jī)的父類扭吁。面向?qū)ο缶褪沁@樣一層一層的向上新建父類最終得到一個“超級父類”在OC和Swift中是NSObject,盡管問題得到了解決盲镶,但是麻雀侥袜、飛機(jī)之間的共性并沒有得到很好的體現(xiàn)。而協(xié)議的出現(xiàn)正是為了解決這類問題溉贿。

    寵物類圖

    事實(shí)上寵物類圖中包括動物枫吧、飛機(jī)等類之間的關(guān)系就應(yīng)該是如上圖所示的簡單繼承關(guān)系宇色。使用協(xié)議將“寵物”九杂、“飛翔”等關(guān)系看作是一種特性颁湖,或者是從另一個維度描述這種類別,更重要的是使用協(xié)議并不會打破原有類別之間繼承的父子關(guān)系例隆。和飛翔相關(guān)的代碼統(tǒng)一放在Flyable中甥捺,需要“飛翔”這種能力遵守該協(xié)議;和寵物相關(guān)的代碼統(tǒng)一放在PetType中镀层,需要成為寵物遵守該協(xié)議镰禾。這些協(xié)議靈活多變結(jié)合原有的面向?qū)ο箢愔g固有的繼承關(guān)系,完美的描述了這個世界唱逢。這幅包含了協(xié)議的寵物類圖是本人在學(xué)習(xí)中印象最深刻的吴侦,分享出來與大家共勉。
    Swift中的協(xié)議更多的時候是在描述某種屬性坞古,是否應(yīng)該將“寵物”設(shè)計成一個類或者是一個協(xié)議备韧,應(yīng)該根據(jù)當(dāng)前項(xiàng)目的需求。如果你的世界沒有麻雀痪枫、飛機(jī)盯蝴,那么將“寵物”設(shè)計成一個類也是未嘗不可甚至是非常合理的,這點(diǎn)需要我們仔細(xì)思考听怕。

  • 面向協(xié)議編程范式
    學(xué)習(xí)使用協(xié)議就不得不提到通過協(xié)議語法延伸出來的 “面向協(xié)議編程范式”捧挺,蘋果提出Swift是一門支持面向協(xié)議編程的語言,甚至提倡我們通過協(xié)議尿瞭、結(jié)構(gòu)體代替部分類的使用闽烙,可見協(xié)議這種語法以及面向協(xié)議編程思想在Swift中是多么的重要。在這里由于筆者水平有限就不對此展開討論声搁,不過在學(xué)習(xí)中收集了幾篇關(guān)于“使用協(xié)議編程”方面的文章黑竞,有興趣的同學(xué)可以參考一下。
    1疏旨,從 Swift 的面向協(xié)議編程說開去
    2很魂,我從55個Swift標(biāo)準(zhǔn)庫協(xié)議中學(xué)到了什么?
    3,Swift面向協(xié)議編程初探

文章最后

以上就是本人關(guān)于協(xié)議語法的心得檐涝,示例代碼在Swift3.0語法下都是編譯通過的遏匆,知識點(diǎn)比較雜,部分描述引自官方的文檔谁榜,另外協(xié)議作為一種強(qiáng)大的語法肯定還有很多值得我們?nèi)ヌ剿鞣福疚牧谐龅闹R點(diǎn)僅僅涵括了部分內(nèi)容。如果文中有任何紕漏或錯誤歡迎在評論區(qū)留言指出窃植,本人將在第一時間修改過來帝蒿;喜歡我的文章,可以關(guān)注我以此促進(jìn)交流學(xué)習(xí)巷怜; 如果覺得此文戳中了你的G點(diǎn)請隨手點(diǎn)贊葛超;轉(zhuǎn)載請注明出處暴氏,謝謝支持。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末绣张,一起剝皮案震驚了整個濱河市答渔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌胖替,老刑警劉巖研儒,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異独令,居然都是意外死亡端朵,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門燃箭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來冲呢,“玉大人,你說我怎么就攤上這事招狸【赐兀” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵裙戏,是天一觀的道長乘凸。 經(jīng)常有香客問我,道長累榜,這世上最難降的妖魔是什么营勤? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮壹罚,結(jié)果婚禮上葛作,老公的妹妹穿的比我還像新娘。我一直安慰自己猖凛,他們只是感情好赂蠢,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著辨泳,像睡著了一般虱岂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上漠吻,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天量瓜,我揣著相機(jī)與錄音,去河邊找鬼途乃。 笑死,一個胖子當(dāng)著我的面吹牛扔傅,可吹牛的內(nèi)容都是我干的耍共。 我是一名探鬼主播烫饼,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼试读!你這毒婦竟也來了杠纵?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤钩骇,失蹤者是張志新(化名)和其女友劉穎比藻,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體倘屹,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡银亲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了纽匙。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片务蝠。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖烛缔,靈堂內(nèi)的尸體忽然破棺而出馏段,到底是詐尸還是另有隱情,我是刑警寧澤践瓷,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布院喜,位于F島的核電站,受9級特大地震影響晕翠,放射性物質(zhì)發(fā)生泄漏喷舀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一崖面、第九天 我趴在偏房一處隱蔽的房頂上張望元咙。 院中可真熱鬧,春花似錦巫员、人聲如沸庶香。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赶掖。三九已至,卻和暖如春七扰,著一層夾襖步出監(jiān)牢的瞬間奢赂,已是汗流浹背颈走。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工膳灶, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留轧钓,地道東北人序厉。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像毕箍,于是被迫代替她去往敵國和親弛房。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容