類胚想、結(jié)構(gòu)體或枚舉都可以遵循協(xié)議尼啡,并為協(xié)議定義的這些要求提供具體實(shí)現(xiàn)暂衡。
除了遵循協(xié)議的類型必須實(shí)現(xiàn)的要求外,還可以對(duì)協(xié)議進(jìn)行擴(kuò)展崖瞭,通過擴(kuò)展來實(shí)現(xiàn)一部分要求或者實(shí)現(xiàn)一些附加功能狂巢,這樣遵循協(xié)議的類型就能夠使用這些功能。
protocol SomeProtocol {
// 這里是協(xié)議的定義部分
}
擁有父類的類在遵循協(xié)議時(shí)读恃,應(yīng)該將父類名放在協(xié)議名之前隧膘,以逗號(hào)分隔。
屬性
協(xié)議不指定屬性是存儲(chǔ)型屬性還是計(jì)算型屬性寺惫,協(xié)議指定的是屬性的名稱和類型疹吃,以及屬性是只讀還是讀寫。
要求可讀寫西雀,實(shí)現(xiàn)可以是讀寫萨驶。如果只要求了可讀,可以實(shí)現(xiàn)為可讀艇肴,也可實(shí)現(xiàn)為可讀寫腔呜。
protocol SomeProtocol {
var mustBeSettable: Int { get set } // 讀寫
var doseNotNeedToBeSettable: Int { get } // 只讀 或者 讀寫
}
協(xié)議中的類型屬性:
在協(xié)議中定義類型屬性時(shí),總是使用 static 關(guān)鍵字作為前綴再悼。當(dāng)類類型遵循協(xié)議時(shí)核畴,除了 static 關(guān)鍵字,還可以使用 class 關(guān)鍵字來聲明類型屬性冲九。
protocol AnotherProtocol {
static var someTypeProperty: Int { get set }
}
方法
類類型的方法常用static修飾谤草,需要類遵守的用class。
protocol SomeProtocol {
static func someTypeMethod()
}
protocol RandomNumberGenerator {
func random() -> Double
}
可變方法
如果你在協(xié)議中定義了一個(gè)實(shí)例方法,該方法會(huì)改變遵循該協(xié)議的類型的實(shí)例丑孩,那么在定義協(xié)議時(shí)需要在方法前加 mutating 關(guān)鍵字冀宴。這使得結(jié)構(gòu)體和枚舉能夠遵循此協(xié)議并滿足此方法要求。
實(shí)現(xiàn)協(xié)議中的 mutating 方法時(shí)温学,若是類類型略贮,則不用寫 mutating 關(guān)鍵字。而對(duì)于結(jié)構(gòu)體和枚舉仗岖,則必須寫 mutating 關(guān)鍵字逃延。
protocol Togglable {
mutating func toggle()
}
構(gòu)造器要求
協(xié)議可以要求遵循協(xié)議的類型實(shí)現(xiàn)指定的構(gòu)造器。
類類型的話轧拄,實(shí)現(xiàn)構(gòu)造器真友,需要加上required,以保證子類繼承的時(shí)候紧帕,也能夠遵守協(xié)議。如果類被標(biāo)記為final桅打,則不需要使用required是嗜,因?yàn)椴粫?huì)再有子類了。
protocol SomeProtocol {
init(someParameter: Int)
}
class SomeClass: SomeProtocol {
required init(someParameter: Int) {
// 這里是構(gòu)造器的實(shí)現(xiàn)部分
}
}
如果一個(gè)子類重寫了父類的指定構(gòu)造器挺尾,并且該構(gòu)造器滿足了某個(gè)協(xié)議的要求鹅搪,那么該構(gòu)造器的實(shí)現(xiàn)需要同時(shí)標(biāo)注 required 和 override 修飾符。
繼承自父類的構(gòu)造器遭铺,滿足某個(gè)協(xié)議丽柿,則required 和 override同時(shí)使用。
可失敗構(gòu)造器要求
協(xié)議還可以為遵循協(xié)議的類型定義可失敗構(gòu)造器要求魂挂。
遵循協(xié)議的類型可以通過可失敗構(gòu)造器(init?)或非可失敗構(gòu)造器(init)來滿足協(xié)議中定義的可失敗構(gòu)造器要求甫题。協(xié)議中定義的非可失敗構(gòu)造器要求可以通過非可失敗構(gòu)造器(init)或隱式解包可失敗構(gòu)造器(init!)來滿足。
協(xié)議作為類型
盡管協(xié)議本身并未實(shí)現(xiàn)任何功能涂召,但是協(xié)議可以被當(dāng)做一個(gè)成熟的類型來使用坠非。
協(xié)議可以和其他類型一樣使用:
- 作為函數(shù)方法構(gòu)造器的參數(shù)類型或者返回值類型
- 作為常量、變量或者屬性的類型
- 作為數(shù)組果正、字典等其他容器的元素類型
class Dice {
let sides: Int
let generator: RandomNumberGenerator
init(sides: Int ,generator: RandomNumberGenerator) {
self.sides = sides
self.generator = generator
}
func roll() -> Int {
return Int(generator.random() * Double(sides)) + 1
}
}
任何遵循了RandomNumberGenerator的實(shí)例炎码,都可以賦值給generator,除此之外秋泳,并無其他要求潦闲。
委托
委托是一種設(shè)計(jì)模式,它允許類或結(jié)構(gòu)體將一些需要它們負(fù)責(zé)的功能委托給其他類型的實(shí)例迫皱。
委托模式的實(shí)現(xiàn)很簡單:定義協(xié)議來封裝那些需要被委托的功能歉闰,這樣就能確保遵循協(xié)議的類型能提供這些功能。
委托模式可以用來響應(yīng)特定的動(dòng)作,或者接收外部數(shù)據(jù)源提供的數(shù)據(jù)新娜,而無需關(guān)心外部數(shù)據(jù)源的類型赵辕。
在擴(kuò)展里添加協(xié)議遵循
通過擴(kuò)展令已有類型遵循并符合協(xié)議時(shí),該類型的所有實(shí)例也會(huì)隨之獲得協(xié)議中定義的各項(xiàng)功能概龄。
有條件地遵循協(xié)議
泛型類型可能只在某些情況下滿足一個(gè)協(xié)議的要求还惠,比如當(dāng)泛型的類型形式參數(shù)遵循對(duì)應(yīng)協(xié)議時(shí)∷蕉牛可以通過在擴(kuò)展類型時(shí)列出限制蚕键,讓泛型類型有條件的遵守某協(xié)議。在你采納協(xié)議的名字后面寫泛型where分句衰粹。
在擴(kuò)展里聲明采納協(xié)議
當(dāng)一個(gè)類型已經(jīng)符合了協(xié)議中的所有要求時(shí)锣光,卻還沒有聲明采納該協(xié)議,可以通過空擴(kuò)展提采納該協(xié)議铝耻。
struct Hamster {
var name: String
var textualDescription: String {
return "A hamster name \(name)"
}
}
extension Hamster: TextRepresentable {}
即使?jié)M足了協(xié)議的所有要求誊爹,類型也不會(huì)自動(dòng)遵循協(xié)議,必須顯式地遵循協(xié)議瓢捉。
協(xié)議類型的集合
協(xié)議類型可以在數(shù)組或者字典這樣的集合中使用频丘。
let things: [TextRepresentable] = [game, d12, simonTheHamster]
協(xié)議的繼承
協(xié)議能夠繼承一個(gè)或多個(gè)其他協(xié)議,可以在繼承的協(xié)議的基礎(chǔ)上增加新的要求泡态。
protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
// 這里是協(xié)議的定義部分
}
遵循繼承的協(xié)議搂漠,需要滿足繼承的所有協(xié)議。
類專屬的協(xié)議
通過添加 AnyObject 關(guān)鍵字到協(xié)議的繼承列表某弦,就可以限制協(xié)議只能被類類型采納(以及非結(jié)構(gòu)體或者非枚舉的類型)桐汤。
protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol {
// 這里是類專屬協(xié)議的定義部分
}
添加AnyObject關(guān)鍵字。
當(dāng)協(xié)議定義的要求需要遵循協(xié)議的類型必須是引用語義而非值語義時(shí)靶壮,應(yīng)該采用類類型專屬協(xié)議怔毛。
協(xié)議合成
使用協(xié)議組合來復(fù)合多個(gè)協(xié)議到一個(gè)要求里。
協(xié)議組合使用 SomeProtocol & AnotherProtocol 的形式亮钦。你可以列舉任意數(shù)量的協(xié)議馆截,用和符號(hào)(&)分開。除了協(xié)議列表蜂莉,協(xié)議組合也能包含類類型蜡娶,這允許你標(biāo)明一個(gè)需要的父類。
例如:組合協(xié)議作為函數(shù)參數(shù)的類型映穗,函數(shù)參數(shù)可以傳遞任何遵循這2個(gè)協(xié)議的類型實(shí)例窖张。
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)!")
}
也可以是類和協(xié)議的組合。
檢查類型的一致性
可以使用類型轉(zhuǎn)換中的is 和 as來判斷協(xié)議的一致性蚁滋,即是否符合某協(xié)議宿接,并且可以轉(zhuǎn)換到指定協(xié)議類型赘淮。
- is 用來檢查實(shí)例是否符合某個(gè)協(xié)議,若符合則返回 true睦霎,否則返回 false梢卸。
- as? 返回一個(gè)可選值,當(dāng)實(shí)例符合某個(gè)協(xié)議時(shí)副女,返回類型為協(xié)議類型的可選值蛤高,否則返回 nil。
- as! 將實(shí)例強(qiáng)制向下轉(zhuǎn)換到某個(gè)協(xié)議類型碑幅,如果強(qiáng)轉(zhuǎn)失敗戴陡,會(huì)引發(fā)運(yùn)行時(shí)錯(cuò)誤。
let objects: [AnyObject] = [Circle(radius: 2.0), Country(area: 22323), Animal(legs: 4)]
for object in objects {
if let objectWithArea = object as? HasArea {
print("\(objectWithArea.area)")
} else {
print("222")
}
}
可選的協(xié)議要求
使用optional關(guān)鍵字來指定沟涨。
可選要求用在你需要和 Objective-C 打交道的代碼中恤批。協(xié)議和可選要求都必須帶上 @objc 屬性。標(biāo)記 @objc 特性的協(xié)議只能被繼承自 Objective-C 類的類或者 @objc 類遵循裹赴,其他類以及結(jié)構(gòu)體和枚舉均不能遵循這種協(xié)議喜庞。
使用可選要求時(shí)(例如,可選的方法或者屬性)棋返,它們的類型會(huì)自動(dòng)變成可選的赋荆。比如,一個(gè)類型為 (Int) -> String 的方法會(huì)變成 ((Int) -> String)?懊昨。需要注意的是整個(gè)函數(shù)類型是可選的,而不是函數(shù)的返回值春宣。
@objc protocol CounterDataSource {
@objc optional func incre(count: Int) -> Int
@objc optional var fixedIncrement: Int { get }
}
協(xié)議擴(kuò)展
協(xié)議可以通過擴(kuò)展來為遵循協(xié)議的類型提供屬性酵颁、方法以及下標(biāo)的實(shí)現(xiàn)≡碌郏可以基于協(xié)議來實(shí)現(xiàn)一些功能躏惋,而不用在遵守協(xié)議的每個(gè)類型中實(shí)現(xiàn)功能。
通過協(xié)議擴(kuò)展嚷辅,所有遵循協(xié)議的類型簿姨,都能自動(dòng)獲得這個(gè)擴(kuò)展所增加的方法實(shí)現(xiàn),無需任何額外修改簸搞。
如果遵循協(xié)議的類型為這些要求提供了自己的實(shí)現(xiàn)扁位,那么這些自定義實(shí)現(xiàn)將會(huì)替代擴(kuò)展中的默認(rèn)實(shí)現(xiàn)被使用。
為協(xié)議擴(kuò)展添加限制條件
在擴(kuò)展協(xié)議的時(shí)候趁俊,可以指定一些限制條件域仇,只有遵循協(xié)議的類型滿足這些限制條件時(shí),才能獲得協(xié)議擴(kuò)展提供的默認(rèn)實(shí)現(xiàn)寺擂。這些限制條件寫在協(xié)議名之后暇务,使用 where
子句來描述泼掠。
extension Collection where Element: Equatable {
func allEqual() -> Bool {
for element in self {
if element != self.first {
return false
}
}
return true
}
}