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)染服。