Swift 中的協(xié)議和擴(kuò)展

協(xié)議

  • 協(xié)議的語法
  1. 自定義類型聲明時(shí),將協(xié)議名放在類型名的冒號(hào)之后來表示該類型采納一個(gè)特定的協(xié)議。多個(gè)協(xié)議可以用都好分開列出
  2. 若一個(gè)類擁有父類扎阶,將這個(gè)父類名放在其采納的謝一鳴錢躲胳,并用都好分隔
protocol SomeProtocol {
    
}
protocol OneProtocol {
    
}

struct SomeStructure: SomeProtocol {
    
}
class SomeStre: SomeProtocol, OneProtocol {
    
}

  • 屬性要求
  1. 協(xié)議可以要求所有遵循該協(xié)議的類型提供特定名字和類型的實(shí)例屬性或類型屬性。協(xié)議并不會(huì)具體說明屬性是存儲(chǔ)型屬性還是計(jì)算型屬性--它只具體要求屬性有特定的名稱和類型胯努。協(xié)議同事要求一個(gè)屬性必須明確是可讀的牢裳,或可讀的和可寫的
  2. 若協(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")
  • 屬性要求
  1. 在協(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 }
}
  • 方法要求
  1. 協(xié)議可以要求采納的類型實(shí)現(xiàn)指定的實(shí)例方法和類方法。這些方法作為協(xié)議定義的一部分晌畅,書寫方式與正常實(shí)例和類方法的方式完全相同但指,但是不需要大括號(hào)和方法的主體。允許擁有參數(shù)抗楔,與正常的方法使用同樣的規(guī)則棋凳。但在協(xié)議的定義中,方法參數(shù)不能定義默認(rèn)值连躏。
  2. 正如類型屬性要求的那樣贫橙,當(dāng)協(xié)議中定義類型方法時(shí),你總要在其之前添加 static 關(guān)鍵字反粥。即使在類實(shí)現(xiàn)時(shí)卢肃,類型方法要求使用 class 或 static 作為關(guān)鍵字前綴,簽名的規(guī)則仍然適用才顿。
  • mutating 方法要求
  1. 若你定義了一個(gè)協(xié)議的實(shí)例方法需求莫湘,想要異變?nèi)魏尾捎昧嗽搮f(xié)議的類型實(shí)例,只需在協(xié)議里方法的定義當(dāng)中使用 mutating 關(guān)鍵字郑气。這允許結(jié)構(gòu)體和枚舉類型能采用相應(yīng)協(xié)議并滿足方法要求
  • 初始化器要求
  1. 協(xié)議寫在要求遵循協(xié)議的類型實(shí)現(xiàn)指定的初始化器幅垮。和一般的初始化器一樣,只用將初始化器寫在協(xié)議的定義當(dāng)中尾组,只是不用寫大括號(hào)也就是初始化器的實(shí)體
protocol SomeProtocol {
    init(someParameter: Int)  
}

  • 初始化器要求的類實(shí)現(xiàn)
  1. 你可以通過實(shí)現(xiàn)指定初始化器或便捷初始化器來遵循該協(xié)議的類滿足協(xié)議的初始化器要求忙芒。在這兩種情況下,你都必須使用 required 關(guān)鍵字修飾初始化器的實(shí)現(xiàn)讳侨。
protocol SomeProtocol {
    init(someParameter: Int)
}
class SomeClass: SomeProtocol {
    required init(someParameter: Int) {
        
    }
}
  1. 如果一個(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() {
        
    }
}
  • 將協(xié)議作為類型
  1. 在函數(shù)潮峦、方法或初始化器里作為形式參數(shù)類型或者返回類型
  2. 作為常量、變量或者屬性的類型勇婴;
  3. 作為數(shù)組忱嘹、字典或者其他存儲(chǔ)器的元素的類型
  • 協(xié)議繼承
  1. 協(xié)議可以繼承一個(gè)或者多個(gè)其他協(xié)議并且可以在它繼承的基礎(chǔ)上添加更多要求。協(xié)議繼承的語法與類繼承的語法相似耕渴,只不過可以選擇列出多個(gè)繼承的協(xié)議拘悦,使用逗號(hào)分隔。
protocol SomeProtocol { 
    
}
protocol AnotherProtocol {
    
}
protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
    
}
  • 類專用的協(xié)議
  1. 通過添加 AnyObject 關(guān)鍵字到協(xié)議的繼承列表橱脸,你就可以限制協(xié)議只能被類類型采納(并且不是結(jié)構(gòu)體或枚舉)础米。
protocol AnotherProtocol {
    
} 

protocol SomeClassOnlyProtocol: AnyObject, AnotherProtocol {
    
}
  • 協(xié)議組合
  1. 可以使用協(xié)議組合來復(fù)用多個(gè)協(xié)議到一個(gè)要求里分苇。協(xié)議組合行為就和你定義的臨時(shí)局部協(xié)議已于,擁有構(gòu)成中所有協(xié)議的需求椭盏。協(xié)議組合補(bǔ)丁已任何新的協(xié)議類型组砚。
  2. 協(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é)議要求
  1. 你可以給協(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ò)展里添加協(xié)議遵循
  1. 你可以擴(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)
  • 有條件的遵循協(xié)議
  1. 泛型類型可能只在某些情況下滿足一個(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)
  • 使用擴(kuò)展聲明采納協(xié)議
  1. 如果一個(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ò)展
  1. 協(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: ",")
    }
}
  • 提供默認(rèn)實(shí)現(xiàn)
  1. 你可以使用協(xié)議擴(kuò)展來給協(xié)議的任意方法或者計(jì)算屬性要求提供默認(rèn)實(shí)現(xiàn)。如果遵循類型給這個(gè)協(xié)議的要求提供了它自己的實(shí)現(xiàn)或南,那么它就會(huì)替代擴(kuò)展中提供的默認(rèn)實(shí)現(xiàn)
  • 給協(xié)議擴(kuò)展添加限制
  1. 當(dāng)你定義一個(gè)協(xié)議擴(kuò)展孩等,你可以明確遵循類型必須在擴(kuò)展的方法和屬性可用之前滿足的限制。在擴(kuò)展協(xié)議名字后邊使用 where 分句來寫這些限制采够。

協(xié)議的關(guān)聯(lián)類型

  • 關(guān)聯(lián)類型
  1. 定義一個(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)用
  1. 這個(gè)協(xié)議沒有指定元素如何儲(chǔ)存在容器中隅要,也沒指定允許存入容器的元素類型蝴罪。協(xié)議僅僅指定了想成為一個(gè) Container 的類型,必須提供的三種功能步清。遵循該協(xié)議的類型可以提供其他功能要门,只要滿足這三個(gè)要求即可
  2. 任何遵循 Container 協(xié)議的類型必須能指定其存儲(chǔ)值的類型。尤其是它必須保證只有正確類型的元素才能添加到容器中廓啊,而且該類型下標(biāo)返回的元素類型必須是正確的欢搜。
  3. 為了定義這些要求, 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]
    }
}
  • 關(guān)聯(lián)類型的約束
  1. 你可以在協(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é)議
  1. 協(xié)議可以作為它自身的要求出現(xiàn)
protocol SuffixableContainer: Container {
    associatedtype Suffix: SuffixableContainer where Suffix.ItemType == ItemType
    func suffix(_ size: Int) -> Suffix
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市粘都,隨后出現(xiàn)的幾起案子廓推,更是在濱河造成了極大的恐慌,老刑警劉巖驯杜,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件受啥,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡鸽心,警方通過查閱死者的電腦和手機(jī)滚局,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來顽频,“玉大人藤肢,你說我怎么就攤上這事∨淳埃” “怎么了嘁圈?”我有些...
    開封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蟀淮。 經(jīng)常有香客問我最住,道長(zhǎng),這世上最難降的妖魔是什么怠惶? 我笑而不...
    開封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任涨缚,我火速辦了婚禮,結(jié)果婚禮上策治,老公的妹妹穿的比我還像新娘脓魏。我一直安慰自己兰吟,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開白布茂翔。 她就那樣靜靜地躺著混蔼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪珊燎。 梳的紋絲不亂的頭發(fā)上惭嚣,一...
    開封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音俐末,去河邊找鬼料按。 笑死奄侠,一個(gè)胖子當(dāng)著我的面吹牛卓箫,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播垄潮,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼烹卒,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了弯洗?” 一聲冷哼從身側(cè)響起旅急,我...
    開封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎牡整,沒想到半個(gè)月后藐吮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡逃贝,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年谣辞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沐扳。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡泥从,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出沪摄,到底是詐尸還是另有隱情躯嫉,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布杨拐,位于F島的核電站祈餐,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏哄陶。R本人自食惡果不足惜帆阳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望奕筐。 院中可真熱鬧舱痘,春花似錦变骡、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至旬盯,卻和暖如春台妆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背胖翰。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工接剩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人萨咳。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓懊缺,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親培他。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鹃两,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

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

  • Swift 中的協(xié)議協(xié)議是為方法、屬性等定義一套規(guī)范舀凛,沒有具體的實(shí)現(xiàn)俊扳,類似于Java中的抽象接口,它只是描述了方法...
    Bobby0322閱讀 4,821評(píng)論 0 1
  • extension 擴(kuò)展為現(xiàn)有的類猛遍、結(jié)構(gòu)體馋记、枚舉類型、或協(xié)議添加了新功能懊烤。這也包括了為無訪問權(quán)限的源代碼擴(kuò)展類型的...
    孤雁_南飛閱讀 1,172評(píng)論 0 8
  • 協(xié)議 協(xié)議的特點(diǎn)1. 協(xié)議可以定義方法、屬性寄啼、下標(biāo)的聲明逮光,協(xié)議可以被枚舉、結(jié)構(gòu)體墩划、類遵守(多個(gè)協(xié)議用逗號(hào)分割)2....
    a_只羊閱讀 657評(píng)論 0 1
  • 實(shí)例屬性涕刚,實(shí)例方法,類方法乙帮,運(yùn)算符杜漠,下標(biāo) 語法 屬性 指定屬性的 名字 和 類型,以及 getterable or...
    樂人曹閱讀 285評(píng)論 0 1
  • 定義: 協(xié)議定義了一個(gè)藍(lán)圖,規(guī)定了用來實(shí)現(xiàn)某一特定的任務(wù)或者功能的方法驾茴、屬性盼樟,或其他需要的東西。類锈至、結(jié)構(gòu)體晨缴、枚舉都...
    geekLiu閱讀 1,443評(píng)論 0 1