Swift中的協(xié)議以及如何使用擴(kuò)展為協(xié)議提供默認(rèn)實(shí)現(xiàn)

Swift中的協(xié)議和OC的協(xié)議基本相同歧匈,都是規(guī)定了用來(lái)實(shí)現(xiàn)某一特定任務(wù)或者功能垒酬,不過(guò)在Swift中可以添加需要實(shí)現(xiàn)的屬性和其它東西,可以說(shuō)Swift的協(xié)議比OC強(qiáng)大的多件炉。Swift中的類(lèi)勘究、結(jié)構(gòu)體和枚舉都可以遵守協(xié)議,并且提供協(xié)議要求的具體實(shí)現(xiàn)斟冕。除了這些還可以使用擴(kuò)展為協(xié)議提供一部分的默認(rèn)實(shí)現(xiàn)口糕,從而使遵守協(xié)議的類(lèi)型直接就可以使用這些功能。

定義協(xié)議

定義協(xié)議的方法很簡(jiǎn)單磕蛇,使用關(guān)鍵字 protocol 后邊跟著協(xié)議的名字景描,名字要使用大駝峰命名法:

protocol SomeProtocol {
    // 這里是協(xié)議的定義部分
}

如果需要繼承自某個(gè)協(xié)議,需要:

protocol SomeProtocol: AnotherProtocol {

}

繼承多個(gè)協(xié)議的話(huà)秀撇,多個(gè)協(xié)議之間用逗號(hào)隔開(kāi)超棺。

我們也可以定義可選的方法,使用在方法前邊添加關(guān)鍵字 optional 來(lái)聲明可選方法呵燕。如果方法只允許OC的類(lèi)或者繼承自O(shè)C的類(lèi)遵守棠绘,則需要在前邊添加關(guān)鍵字 @objc

遵循協(xié)議

遵循協(xié)議的時(shí)候只需要在類(lèi)型名稱(chēng)后邊加上要遵循的協(xié)議名稱(chēng)再扭,并以冒號(hào)分割就好了:

class SomeClass: SomeProtocol {
// 這里是類(lèi)的定義部分
}

如果有父類(lèi)的話(huà)氧苍,把父類(lèi)寫(xiě)在最前邊。

屬性和方法要求

Swift中的協(xié)議可以添加屬性要求泛范,要求遵守協(xié)議的類(lèi)让虐,結(jié)構(gòu)體,枚舉必須提供特定名稱(chēng)和類(lèi)型的實(shí)例或者類(lèi)型屬性罢荡。在協(xié)議中添加屬性赡突,需要指明屬性是只讀還是可讀寫(xiě)的对扶。如果是只讀的屬性,那么遵從協(xié)議的類(lèi)型麸俘,可以根據(jù)需要將其實(shí)現(xiàn)為只讀或可讀寫(xiě)的辩稽,如果是可讀寫(xiě)的則不能實(shí)現(xiàn)為只讀的惧笛。

協(xié)議中定義屬性時(shí)應(yīng)該聲明為變量从媚,而不能聲明為常量,因?yàn)檫@些屬性都是計(jì)算型屬性患整,不是存儲(chǔ)型屬性拜效。協(xié)議中聲明類(lèi)型屬性的時(shí)候使用關(guān)鍵字 static ,類(lèi)型遵守協(xié)議時(shí)也可以使用 class 聲明要求的屬性各谚。

protocol SomeProtocol {
    var propertyA: Int { get set }
    var propertyB: Int { get }
    static var typePropertyA: Int { get set }
}

class SomeClass: SomeProtocol {
    var propertyA: Int {
        //這里只能是可讀寫(xiě)的紧憾,不能是只讀的
        set {
        }
        get {
        }
    }
    var propertyB: Int {
        //可以根據(jù)需要,聲明成只讀或者可讀寫(xiě)
    }
    //也可以使用 class 替代 static
    static var typeProperty: Int {
    }
}

協(xié)議中的類(lèi)型方法跟類(lèi)型屬性一樣昌渤,前邊需要 使用 static 關(guān)鍵字赴穗,類(lèi)型遵循協(xié)議的時(shí)候可以使用關(guān)鍵字 class

在值類(lèi)型的實(shí)例方法中修改實(shí)例屬性

我們知道如果需要在值類(lèi)型(結(jié)構(gòu)體膀息、枚舉)中修改實(shí)例或者實(shí)例的屬性般眉,需要添加關(guān)鍵字 mutating ,所以如果我們的協(xié)議方法允許修改值類(lèi)型的實(shí)例或者值類(lèi)型的實(shí)力屬性潜支,我們應(yīng)該在協(xié)議中實(shí)例方法前面添加 mutating 關(guān)鍵字甸赃,否則遵守該協(xié)議的值類(lèi)型,將無(wú)法在實(shí)例方法中修改實(shí)例或者實(shí)例屬性冗酿。

protocol MutatingChange {
    mutating func change()
}

通過(guò)協(xié)議添加構(gòu)造器

如果需要遵循協(xié)議的類(lèi)型實(shí)現(xiàn)指定的構(gòu)造器埠对,則遵循協(xié)議的類(lèi)必須在要實(shí)現(xiàn)的構(gòu)造器的前邊添加 required 關(guān)鍵字,這樣確保所有子類(lèi)也都提供了此構(gòu)造器的實(shí)現(xiàn)裁替,也能符合協(xié)議项玛。如果這個(gè)類(lèi)被標(biāo)記為 final 類(lèi),說(shuō)明這個(gè)類(lèi)不能被繼承弱判,則不需要添加 required襟沮。協(xié)議也可以聲明可失敗構(gòu)造器。協(xié)議中的可失敗構(gòu)造器可以通過(guò)遵循協(xié)議類(lèi)型中的可失敗或非可失敗構(gòu)造器滿(mǎn)足裕循,而協(xié)議中的非可失敗構(gòu)造器只能通過(guò)遵循協(xié)議類(lèi)型中的非可失敗或者隱式解包構(gòu)造起來(lái)滿(mǎn)足臣嚣。

協(xié)議可作為類(lèi)型使用

協(xié)議是可以作為類(lèi)型使用的,可以被函數(shù)返回剥哑,也可以作為參數(shù)傳遞硅则。

var protocolA: SomeProtocol
func printProtocol(theProtocol: SomeProtocol){
}

通過(guò)擴(kuò)展遵循某個(gè)協(xié)議

當(dāng)我們要遵循一個(gè)協(xié)議的時(shí)候,我們可以通過(guò)擴(kuò)展來(lái)遵循協(xié)議株婴,這樣做的好處是可以將代碼分開(kāi)怎虫,提高代碼的可讀性暑认。例如:

extension SomeClass: SomeProtocol {
    //協(xié)議方法的實(shí)現(xiàn)
}

類(lèi)類(lèi)型專(zhuān)屬協(xié)議

如果我們只需要類(lèi)類(lèi)型能遵守協(xié)議,其他類(lèi)型不能遵守協(xié)議大审,那么我們需要用關(guān)鍵字 class 來(lái)指定這個(gè)協(xié)議時(shí)類(lèi)類(lèi)型專(zhuān)屬的協(xié)議蘸际。

protocol classOnlyProtocol: class, SomeProtocol {
// 這里是類(lèi)類(lèi)型專(zhuān)屬協(xié)議的定義部分
}

遵循多個(gè)協(xié)議的函數(shù)參數(shù)

如果函數(shù)參數(shù)中的某個(gè)參數(shù)需要是遵循多個(gè)協(xié)議的參數(shù),我們?cè)撛趺慈?xiě)呢徒扶,我們只需要把需要遵循的這兩個(gè)協(xié)議使用 & 連接起來(lái)作為參數(shù)類(lèi)型就好了:

func followTwoProtocol(arguments: SomeProtocol & otherProtocol) {

}

如何檢查是否遵循某個(gè)協(xié)議

當(dāng)我們需要檢查某個(gè)實(shí)例是否符合某個(gè)協(xié)議的時(shí)候粮彤,我們可以使用 is 來(lái)判斷,符合返回 true姜骡,不符合返回 false导坟。

有時(shí)候一個(gè)協(xié)議繼承自另一個(gè)協(xié)議,當(dāng)我們使用的時(shí)候可能會(huì)涉及到協(xié)議類(lèi)型的轉(zhuǎn)換圈澈,這時(shí)候我們可以使用 as? 或者 as! 惫周。as?會(huì)返回一個(gè)可選的類(lèi)型,如果轉(zhuǎn)換失敗則返回nil康栈,as!會(huì)進(jìn)行強(qiáng)行轉(zhuǎn)換递递,如果轉(zhuǎn)換失敗會(huì)引發(fā)崩潰。

使用擴(kuò)展為協(xié)議提供默認(rèn)的實(shí)現(xiàn)

Swift中我們可以通過(guò)擴(kuò)展為遵循協(xié)議的類(lèi)型提供屬性啥么、方法以及下標(biāo)的實(shí)現(xiàn)登舞。也就是說(shuō)我們可以基于協(xié)議提供這些功能實(shí)現(xiàn),而不需要每個(gè)遵守協(xié)議的類(lèi)型重復(fù)同樣的實(shí)現(xiàn)饥臂。

protocol DescriptionProtocol {
    func printSomething()
}

extension DescriptionProtocol {
    func printSomething() {
        print("This is a default implementation!")
    }
}

我們聲明了一個(gè)協(xié)議逊躁,并且為協(xié)議方法 printSomething() 添加了默認(rèn)的實(shí)現(xiàn),接下來(lái)我們聲明一個(gè)類(lèi)去遵守這個(gè)協(xié)議隅熙,看能不能直接調(diào)用默認(rèn)的實(shí)現(xiàn):

class SomeClass {
    var name: String?
}

extension SomeClass: DescriptionProtocol {

}
let someone = SomeClass()
someone.printSomething()
//打印出來(lái) "This is a default implementation!"

根據(jù)最后的打印結(jié)果稽煤,我們發(fā)現(xiàn),在我們并未提供實(shí)現(xiàn)的情況下囚戚,調(diào)用了默認(rèn)的實(shí)現(xiàn)酵熙。那如果我們又自己提供了方法的實(shí)現(xiàn)會(huì)使什么樣的呢?

extension SomeClass: DescriptionProtocol {
    func printSomething() {
        print("This is SomeClass's implementation! ")
    }
}
let someone = SomeClass()
someone.printSomething()
//打印出來(lái) "This is SomeClass's implementation!"

所以遵守協(xié)議的類(lèi)自己提供了實(shí)現(xiàn)的時(shí)候驰坊,會(huì)調(diào)用自己提供的實(shí)現(xiàn)匾二。

還有一種情況當(dāng)協(xié)議的的默認(rèn)實(shí)現(xiàn)需要符合一定的條件的類(lèi)型遵守時(shí)才能被調(diào)用,這時(shí)候我們?cè)撛趺醋瞿厝健wift為我們提供了where關(guān)鍵字察藐,這可以讓我們篩選可以使用默認(rèn)實(shí)現(xiàn)的類(lèi)。

class SomeClass {

}

protocol SomeProtocol {
    associatedtype SomeType
    func printSomething()
}

extension SomeProtocol where SomeType: Equatable {
    func printSomething() {
        print("Limit implementation!")
    }
}

extension SomeClass: SomeProtocol {
    typealias SomeType = Int
}
let some = SomeClass()
some.printSomething()

上邊的代碼可以打印出來(lái) "Limit implementation!" 這是因?yàn)殛P(guān)聯(lián)類(lèi)型 SomeType 的類(lèi)型被設(shè)置成了 Int 類(lèi)型舟扎,而 Int類(lèi)型是遵守 Equatable 協(xié)議的分飞,所以可以直接使用默認(rèn)實(shí)現(xiàn),但是如果換成不遵守 Equatable 協(xié)議的類(lèi)型時(shí)睹限,那么默認(rèn)的實(shí)現(xiàn)將是不可用的譬猫。

注意:如果多個(gè)協(xié)議擴(kuò)展都為協(xié)議提供了默認(rèn)實(shí)現(xiàn)讯檐,而遵循協(xié)議的類(lèi)型又同時(shí)滿(mǎn)足這些協(xié)議擴(kuò)展的限制條件,那么將會(huì)使用限制條件最多的那個(gè)協(xié)議擴(kuò)展提供的默認(rèn)實(shí)現(xiàn)染服。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末别洪,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子柳刮,更是在濱河造成了極大的恐慌挖垛,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件诚亚,死亡現(xiàn)場(chǎng)離奇詭異晕换,居然都是意外死亡午乓,警方通過(guò)查閱死者的電腦和手機(jī)站宗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)益愈,“玉大人梢灭,你說(shuō)我怎么就攤上這事≌羝洌” “怎么了敏释?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)摸袁。 經(jīng)常有香客問(wèn)我钥顽,道長(zhǎng),這世上最難降的妖魔是什么靠汁? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任蜂大,我火速辦了婚禮,結(jié)果婚禮上蝶怔,老公的妹妹穿的比我還像新娘奶浦。我一直安慰自己,他們只是感情好踢星,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布澳叉。 她就那樣靜靜地躺著,像睡著了一般沐悦。 火紅的嫁衣襯著肌膚如雪成洗。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,301評(píng)論 1 301
  • 那天藏否,我揣著相機(jī)與錄音瓶殃,去河邊找鬼。 笑死秕岛,一個(gè)胖子當(dāng)著我的面吹牛碌燕,可吹牛的內(nèi)容都是我干的误证。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼修壕,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼愈捅!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起慈鸠,我...
    開(kāi)封第一講書(shū)人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蓝谨,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后青团,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體譬巫,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年督笆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了芦昔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡娃肿,死狀恐怖咕缎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情料扰,我是刑警寧澤凭豪,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站晒杈,受9級(jí)特大地震影響嫂伞,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拯钻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一帖努、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧说庭,春花似錦然磷、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至捆憎,卻和暖如春舅柜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背躲惰。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工致份, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人础拨。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓氮块,卻偏偏與公主長(zhǎng)得像绍载,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子滔蝉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

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