前言
熟悉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
扣孟、name
、birthPlace
,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
,修改s1
的birthPlace
屬性時編譯器就會報錯: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)的赌莺,比如存在TextOne
、TextTwo
兩個協(xié)議松嘶,定義如下:
protocol TextOne {
func text() -> Int
}
protocol TextTwo {
func text() -> String
}
這兩個協(xié)議中的text()
方法名相同返回值不同艘狭,如果存在一個類型Person
同時遵守了TextOne
和TextTwo
,在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)OneProtocol
和 TwoProtocol
的要求必須實(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
繼承自OneProtocol
和TwoProtocol
构哺,然后指定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噸
夸研。
從上面的例子可以很好的看出由于MobilePhone
、Car
稱重時邏輯是一樣的依鸥,但是對于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
類型的屬性math
、english
和一個計算數(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é)議祈惶。
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
表窘、ExpressibleByStringLiteral
、ExpressibleByBooleanLiteral
甜滨、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)載請注明出處暴氏,謝謝支持。