Swift知識(shí)點(diǎn)21 - 協(xié)議

類胚想、結(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é)議可以和其他類型一樣使用:

  1. 作為函數(shù)方法構(gòu)造器的參數(shù)類型或者返回值類型
  2. 作為常量、變量或者屬性的類型
  3. 作為數(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
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市垦细,隨后出現(xiàn)的幾起案子择镇,更是在濱河造成了極大的恐慌,老刑警劉巖括改,帶你破解...
    沈念sama閱讀 222,807評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件腻豌,死亡現(xiàn)場離奇詭異,居然都是意外死亡叹谁,警方通過查閱死者的電腦和手機(jī)饲梭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來焰檩,“玉大人憔涉,你說我怎么就攤上這事∥錾唬” “怎么了兜叨?”我有些...
    開封第一講書人閱讀 169,589評(píng)論 0 363
  • 文/不壞的土叔 我叫張陵,是天一觀的道長衩侥。 經(jīng)常有香客問我国旷,道長,這世上最難降的妖魔是什么茫死? 我笑而不...
    開封第一講書人閱讀 60,188評(píng)論 1 300
  • 正文 為了忘掉前任跪但,我火速辦了婚禮,結(jié)果婚禮上峦萎,老公的妹妹穿的比我還像新娘屡久。我一直安慰自己,他們只是感情好爱榔,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,185評(píng)論 6 398
  • 文/花漫 我一把揭開白布被环。 她就那樣靜靜地躺著,像睡著了一般详幽。 火紅的嫁衣襯著肌膚如雪筛欢。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,785評(píng)論 1 314
  • 那天唇聘,我揣著相機(jī)與錄音版姑,去河邊找鬼。 笑死迟郎,一個(gè)胖子當(dāng)著我的面吹牛漠酿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播谎亩,決...
    沈念sama閱讀 41,220評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼炒嘲,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼宇姚!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起夫凸,我...
    開封第一講書人閱讀 40,167評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤浑劳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后夭拌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體魔熏,經(jīng)...
    沈念sama閱讀 46,698評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,767評(píng)論 3 343
  • 正文 我和宋清朗相戀三年鸽扁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蒜绽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,912評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡桶现,死狀恐怖躲雅,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情骡和,我是刑警寧澤相赁,帶...
    沈念sama閱讀 36,572評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站慰于,受9級(jí)特大地震影響钮科,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜婆赠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,254評(píng)論 3 336
  • 文/蒙蒙 一绵脯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧休里,春花似錦桨嫁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽楣导。三九已至废境,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間筒繁,已是汗流浹背噩凹。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評(píng)論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留毡咏,地道東北人驮宴。 一個(gè)月前我還...
    沈念sama閱讀 49,359評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像呕缭,于是被迫代替她去往敵國和親堵泽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子修己,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,922評(píng)論 2 361

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