協(xié)議
- 自定義類型聲明時(shí),將協(xié)議名放在類型名的冒號(hào)之后來表示該類型采納一個(gè)特定的協(xié)議。多個(gè)協(xié)議可以用都好分開列出
- 若一個(gè)類擁有父類扎阶,將這個(gè)父類名放在其采納的謝一鳴錢躲胳,并用都好分隔
protocol SomeProtocol {
}
protocol OneProtocol {
}
struct SomeStructure: SomeProtocol {
}
class SomeStre: SomeProtocol, OneProtocol {
}
- 協(xié)議可以要求所有遵循該協(xié)議的類型提供特定名字和類型的實(shí)例屬性或類型屬性。協(xié)議并不會(huì)具體說明屬性是存儲(chǔ)型屬性還是計(jì)算型屬性--它只具體要求屬性有特定的名稱和類型胯努。協(xié)議同事要求一個(gè)屬性必須明確是可讀的牢裳,或可讀的和可寫的
- 若協(xié)議要求一個(gè)屬性為可讀和可寫的,那么該屬性要求不能用常量存儲(chǔ)屬性或只讀計(jì)算屬性來滿足叶沛。若協(xié)議只要求屬性為可讀的蒲讯,那么任何種類的屬性都能滿足這個(gè)要求,而且如果你的代碼需要的話灰署,該屬性也可以使可寫的判帮。
protocol SomeProtocol {
var mustBeSettable: Int { get set }
var doesNotNeedToBeSettable: Int {get}
}
protocol FullyNamed {
var fullName: String { get }
}
struct Person: FullyNamed {
var fullName: String
}
let john = Person(fullName: "John AppleSeed")
class Starship: FullyNamed {
var prefix: String?
var name: String
init(name: String, prefix: String? = nil) {
self.name = name
self.prefix = prefix
}
var fullName: String {
return ((prefix != nil) ? (prefix! + "") : "" ) + name
}
}
var ncc = Starship(name: "Enterprise", prefix: "USS")
- 在協(xié)議中定義類型屬性時(shí)局嘁,在前面添加 static 關(guān)鍵字。當(dāng)類的實(shí)現(xiàn)使用 class 或 static 關(guān)鍵字前綴聲明類型屬性要求時(shí)晦墙,這個(gè)規(guī)則仍然適用悦昵。
protocol AnotherProtocol {
static var someTypeProperty: Int { get set }
}
- 協(xié)議可以要求采納的類型實(shí)現(xiàn)指定的實(shí)例方法和類方法。這些方法作為協(xié)議定義的一部分晌畅,書寫方式與正常實(shí)例和類方法的方式完全相同但指,但是不需要大括號(hào)和方法的主體。允許擁有參數(shù)抗楔,與正常的方法使用同樣的規(guī)則棋凳。但在協(xié)議的定義中,方法參數(shù)不能定義默認(rèn)值连躏。
- 正如類型屬性要求的那樣贫橙,當(dāng)協(xié)議中定義類型方法時(shí),你總要在其之前添加 static 關(guān)鍵字反粥。即使在類實(shí)現(xiàn)時(shí)卢肃,類型方法要求使用 class 或 static 作為關(guān)鍵字前綴,簽名的規(guī)則仍然適用才顿。
- 若你定義了一個(gè)協(xié)議的實(shí)例方法需求莫湘,想要異變?nèi)魏尾捎昧嗽搮f(xié)議的類型實(shí)例,只需在協(xié)議里方法的定義當(dāng)中使用 mutating 關(guān)鍵字郑气。這允許結(jié)構(gòu)體和枚舉類型能采用相應(yīng)協(xié)議并滿足方法要求
- 協(xié)議寫在要求遵循協(xié)議的類型實(shí)現(xiàn)指定的初始化器幅垮。和一般的初始化器一樣,只用將初始化器寫在協(xié)議的定義當(dāng)中尾组,只是不用寫大括號(hào)也就是初始化器的實(shí)體
protocol SomeProtocol {
init(someParameter: Int)
}
- 你可以通過實(shí)現(xiàn)指定初始化器或便捷初始化器來遵循該協(xié)議的類滿足協(xié)議的初始化器要求忙芒。在這兩種情況下,你都必須使用 required 關(guān)鍵字修飾初始化器的實(shí)現(xiàn)讳侨。
protocol SomeProtocol {
init(someParameter: Int)
}
class SomeClass: SomeProtocol {
required init(someParameter: Int) {
}
}
- 如果一個(gè)子類重寫了父類指定的初始化器呵萨,并且遵循協(xié)議實(shí)現(xiàn)了初始化器要求,那么就要為這個(gè)初始化器的實(shí)現(xiàn)添加 required 和 override 兩個(gè)修飾符跨跨。
protocol SomeProtocol {
init()
}
class SomeSuperClass {
init() {
}
}
class SomeSubClass: SomeSuperClass, SomeProtocol {
required override init() {
}
}
- 在函數(shù)潮峦、方法或初始化器里作為形式參數(shù)類型或者返回類型
- 作為常量、變量或者屬性的類型勇婴;
- 作為數(shù)組忱嘹、字典或者其他存儲(chǔ)器的元素的類型
- 協(xié)議可以繼承一個(gè)或者多個(gè)其他協(xié)議并且可以在它繼承的基礎(chǔ)上添加更多要求。協(xié)議繼承的語法與類繼承的語法相似耕渴,只不過可以選擇列出多個(gè)繼承的協(xié)議拘悦,使用逗號(hào)分隔。
protocol SomeProtocol {
}
protocol AnotherProtocol {
}
protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
}
- 通過添加 AnyObject 關(guān)鍵字到協(xié)議的繼承列表橱脸,你就可以限制協(xié)議只能被類類型采納(并且不是結(jié)構(gòu)體或枚舉)础米。
protocol AnotherProtocol {
}
protocol SomeClassOnlyProtocol: AnyObject, AnotherProtocol {
}
- 可以使用協(xié)議組合來復(fù)用多個(gè)協(xié)議到一個(gè)要求里分苇。協(xié)議組合行為就和你定義的臨時(shí)局部協(xié)議已于,擁有構(gòu)成中所有協(xié)議的需求椭盏。協(xié)議組合補(bǔ)丁已任何新的協(xié)議類型组砚。
- 協(xié)議組合使用 Name & Aged 的形式吻商。你可以列舉任意數(shù)量的協(xié)議掏颊,用和符號(hào)(&) 連接。處理協(xié)議列表艾帐、協(xié)議組合也能包含類類型乌叶,這允許你標(biāo)明一個(gè)需要的父類
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: "Malolm", age: 21)
wishHappyBirthday(to: birthdayPerson)
- 你可以給協(xié)議定義可選要求,這些要求不需要強(qiáng)制遵循協(xié)議的類型實(shí)現(xiàn)柒爸∽荚。可選要求使用 optional 修飾符作為前綴放在協(xié)議的定義中。 可選要求允許你的代碼與 Objective-C 操作捎稚。協(xié)議和可選要求必須使用 @objc 標(biāo)志標(biāo)記乐横。注意 @objc 協(xié)議只能被繼承自 Objective-C 類或其他 @objc 類采納。他們不能被結(jié)構(gòu)體或者枚舉采納今野。
協(xié)議和擴(kuò)展
- 你可以擴(kuò)展一個(gè)已經(jīng)存在的類型來采納和遵循一個(gè)新的協(xié)議葡公,就算是你無法訪問現(xiàn)有類型的源代碼也行。擴(kuò)展可以添加新的屬性条霜、方法和下標(biāo)到已經(jīng)存在的類型催什,并且因此允許你添加協(xié)議需要的任何需要。
protocol Named {
var name: String { get }
}
protocol Aged {
var age: Int { get }
}
struct Person: Named, Aged {
var name: String
var age: Int
}
protocol TextRepresentable {
var desc: String { get }
}
extension Person: TextRepresentable {
var desc: String {
return "name \(name) age \(age)"
}
}
let p = Person(name: "zhangsan", age: 20)
print(p.desc)
- 泛型類型可能只在某些情況下滿足一個(gè)協(xié)議的要求宰睡,比如當(dāng)類型的泛型形式參數(shù)遵循對(duì)應(yīng)協(xié)議時(shí)蒲凶。你可以通過在擴(kuò)展類型時(shí)列出限制讓泛型類型有條件地遵循某協(xié)議。在你采納協(xié)議的名字后面寫泛型 where 分句
extension Array: TextRepresentable where Element: TextRepresentable {
var desc: String {
let itemDesc = self.map{ $0.desc }
return itemDesc.joined(separator: ",")
}
}
let array = [Person(name: "zhangsan", age: 20), Person(name: "lisi", age: 35)]
print(array.desc)
- 如果一個(gè)類型已經(jīng)遵循了協(xié)議的所有需求拆内,但是還沒有聲明它采納了這個(gè)協(xié)議旋圆,你可以讓通過一個(gè)空的擴(kuò)展來讓它采納這個(gè)協(xié)議
protocol TextRepresentable {
var desc: String { get }
}
struct Hamster {
var name: String
var desc: String {
return "A hamster named \(name)"
}
}
extension Hamster: TextRepresentable{}
- 協(xié)議可以通過擴(kuò)展來提供方法和屬性的實(shí)現(xiàn)以遵循類型。這就允許你在協(xié)議自身定義行為麸恍,而不是在每一個(gè)遵循或者在全局函數(shù)里定義臂聋。
extension Collection where Iterator.Element: TextRepresentable {
var desc: String {
let itemDesc = self.map{ $0.desc }
return itemDesc.joined(separator: ",")
}
}
- 你可以使用協(xié)議擴(kuò)展來給協(xié)議的任意方法或者計(jì)算屬性要求提供默認(rèn)實(shí)現(xiàn)。如果遵循類型給這個(gè)協(xié)議的要求提供了它自己的實(shí)現(xiàn)或南,那么它就會(huì)替代擴(kuò)展中提供的默認(rèn)實(shí)現(xiàn)
- 當(dāng)你定義一個(gè)協(xié)議擴(kuò)展孩等,你可以明確遵循類型必須在擴(kuò)展的方法和屬性可用之前滿足的限制。在擴(kuò)展協(xié)議名字后邊使用 where 分句來寫這些限制采够。
協(xié)議的關(guān)聯(lián)類型
- 定義一個(gè)協(xié)議時(shí)肄方,有時(shí)在協(xié)議定義里聲明一個(gè)或多個(gè)關(guān)聯(lián)類型是很有用的。關(guān)聯(lián)類型給協(xié)議中用到的類型一個(gè)占位符名稱蹬癌。知道采納協(xié)議時(shí)权她,才指定用于該關(guān)聯(lián)類型的實(shí)際類型虹茶。關(guān)聯(lián)類型通過 associatedtype 關(guān)鍵字指定。
- 關(guān)聯(lián)類型的應(yīng)用
- 這個(gè)協(xié)議沒有指定元素如何儲(chǔ)存在容器中隅要,也沒指定允許存入容器的元素類型蝴罪。協(xié)議僅僅指定了想成為一個(gè) Container 的類型,必須提供的三種功能步清。遵循該協(xié)議的類型可以提供其他功能要门,只要滿足這三個(gè)要求即可
- 任何遵循 Container 協(xié)議的類型必須能指定其存儲(chǔ)值的類型。尤其是它必須保證只有正確類型的元素才能添加到容器中廓啊,而且該類型下標(biāo)返回的元素類型必須是正確的欢搜。
- 為了定義這些要求, Container 協(xié)議需要一種在不知道容器具體類型的情況下谴轮,引用該容器將存儲(chǔ)的元素類型的方法炒瘟。 Container 協(xié)議需要制定所有傳給 append(_:) 方法的值必須和容器里元素的值類型是一樣的,而且容器下標(biāo)返回的值也是和容器里元素的值類型相同第步。
protocol Container {
associatedtype ItemType //觀察類型
mutating func append(_ item: ItemType)
var count: Int { get }
subscript(i: Int) -> ItemType { get }
}
struct IntStack: Container {
var items = [Int]()
mutating func push(_ item: Int) {
items.append(item)
}
mutating func pop() -> Int {
return items.removeLast()
}
typealias ItemType = Int
mutating func append(_ item: Int) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Int {
return items[i]
}
}
//泛型
struct Stack<Element>: Container {
var items = [Element]()
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
mutating func append(_ item: Element) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Element {
return items[i]
}
}
- 你可以在協(xié)議里給關(guān)聯(lián)類型添加約束來要求遵循的類型滿足約束疮装。
protocol Container {
associatedtype ItemType: Equatable
mutating func append(_ item: ItemType)
var count: Int { get }
subscript(i: Int) -> ItemType { get }
}
- 在關(guān)聯(lián)類型約束里使用協(xié)議
- 協(xié)議可以作為它自身的要求出現(xiàn)
protocol SuffixableContainer: Container {
associatedtype Suffix: SuffixableContainer where Suffix.ItemType == ItemType
func suffix(_ size: Int) -> Suffix
}