協(xié)議定義了適合特定任務(wù)或功能的方法,屬性。協(xié)議可以由類(lèi),結(jié)構(gòu)或枚舉實(shí)現(xiàn),任何類(lèi)型實(shí)現(xiàn)協(xié)議的要求方法稱(chēng)為遵守協(xié)議。
個(gè)人理解:Swift中的協(xié)議所能實(shí)現(xiàn)的功能,不再局限于OC的代理委托。協(xié)議中定義的方法、屬性,在遵守協(xié)議的類(lèi)型的實(shí)例中可以直接調(diào)用和使用。協(xié)議這種新的能力,使得協(xié)議在Swift中的使用更加的靈活。
Protocol語(yǔ)法
為類(lèi),結(jié)構(gòu)體,枚舉定義協(xié)議的語(yǔ)法
protocol `protocolName` {
//定義協(xié)議
}
自定義類(lèi)型遵守協(xié)議的語(yǔ)法
協(xié)議名稱(chēng)放在類(lèi)型名稱(chēng)之后,并用冒號(hào)分隔,多個(gè)協(xié)議時(shí),協(xié)議之間使用逗號(hào)。當(dāng)類(lèi)類(lèi)型有父類(lèi)時(shí)則父類(lèi)類(lèi)型名稱(chēng)放在協(xié)議之前并用逗號(hào)隔開(kāi)。
- 值類(lèi)型
struct SomeStructure: FirstProtocol, AnotherProtocol {
// 結(jié)構(gòu)體定義
}
- 類(lèi)類(lèi)型
class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol {
// class定義
}
協(xié)議中定義屬性的要求
協(xié)議可以要求任何遵守此協(xié)議的類(lèi)型提供一個(gè)具有特定類(lèi)型和名稱(chēng)的實(shí)例屬性或類(lèi)屬性。但是協(xié)議不能指定屬性應(yīng)該是一個(gè)存儲(chǔ)屬性還是一個(gè)計(jì)算屬性。因此協(xié)議中定義的屬性只能要求屬性的名稱(chēng)與類(lèi)型,并且指定屬性是可get或支持get與set的。
如果協(xié)議要求一個(gè)屬性是可get與set的,那么協(xié)議對(duì)這個(gè)屬性的要求是不能被常量存儲(chǔ)屬性或者只讀的計(jì)算屬性滿(mǎn)足的。
如果協(xié)議僅要求一個(gè)屬性是可get的,那么這個(gè)要求可以被任何類(lèi)型的屬性滿(mǎn)足,同時(shí)如果有必要,這個(gè)屬性也是可set的。
屬性要求:
- 協(xié)議中定義屬性必須始終聲明為變量屬性,并以
var關(guān)鍵字為前綴。通過(guò)在類(lèi)型聲明后寫(xiě){ get set }來(lái)表示屬性可讀寫(xiě),通過(guò)寫(xiě){get}來(lái)表示可讀的屬性。
protocol SomeProtocol {
var mustBeSettable: Int { get set }
var doesNotNeedToBeSettable: Int { get }
}
- 協(xié)議中定義類(lèi)屬性時(shí),必須始終在前面使用
static關(guān)鍵字。即使該協(xié)議的類(lèi)屬性在被類(lèi)實(shí)現(xiàn)時(shí),以class或static為前綴都是符合協(xié)議的。
protocol protocolName {
//定義協(xié)議
static var something : String { get}
}
class ViewController: UIViewController,protocolName {
class var something: String {//使用static也可
"協(xié)議中定義的類(lèi)屬性"
}
}
使用舉例:
定義協(xié)議如下
protocol Category {
var kind : String {get set}
}
類(lèi)實(shí)現(xiàn)
class Animal : Category {
var kind: String
init(kinds:String) {
kind = kinds
}
convenience init(){
self.init(kinds:"??")
}
}
let animal = Animal()
print(animal.kind)//??
注意: 協(xié)議中屬性為{get set}使用計(jì)算屬性時(shí)必須get{}和set{}不能為get{};協(xié)議中屬性為{get}時(shí)則可以使用get{}或者get{}和set{}
class Animal : Category {
var houzhui : String = "類(lèi)"
var kind: String {
get{
houzhui
}
set{
houzhui = newValue + "類(lèi)"
}
}
}
//調(diào)用
let animal = Animal.init()
animal.kind = "??"
print(animal.kind) // ??類(lèi)
結(jié)構(gòu)體實(shí)現(xiàn)
struct AnimalStruct : Category {
var kind: String
}
let animal1 = AnimalStruct(kind:"??")
print(animal1.kind)//??
協(xié)議中定義方法的要求
協(xié)議中方法定義與普通實(shí)例和類(lèi)方法定義方式一樣,并且允許可變參數(shù),遵守與常規(guī)方法相同的規(guī)則 區(qū)別:沒(méi)有花括號(hào)和方法主體;無(wú)法為方法參數(shù)指定默認(rèn)值。會(huì)要求符合此協(xié)議的類(lèi)型實(shí)現(xiàn)協(xié)議中規(guī)定的實(shí)例方法和類(lèi)方法。
回顧可變參數(shù):形式:
Type...,作用可以接受零個(gè)或多個(gè)指定類(lèi)型的值,在函數(shù)體內(nèi)可用作指定類(lèi)型的數(shù)組。
協(xié)議中定義類(lèi)方法時(shí),必須始終在前面使用static關(guān)鍵字。即使該協(xié)議的類(lèi)方法在被類(lèi)實(shí)現(xiàn)時(shí),以class或static為前綴都是符合協(xié)議的。
定義方法協(xié)議
protocol MethodProtocol {
func instanceMethod(para:String) -> String
static func classMethod(para:String)->String
func hasVariableParameter(somePara:String...) -> String
}
實(shí)現(xiàn)協(xié)議方法
class MethodProtocolClass : MethodProtocol {
func hasVariableParameter(somePara: String...) -> String {
var result : String = ""
for item in somePara {
result += item
}
return result
}
func instanceMethod(para: String) -> String {
para + "實(shí)例方法協(xié)議實(shí)現(xiàn)"
}
class func classMethod(para: String) -> String {//!< static也可以
para + "類(lèi)方法協(xié)議實(shí)現(xiàn)"
}
}
//調(diào)用
let instance = MethodProtocolClass()
print(instance.instanceMethod(para: "hello!"))//!< hello!實(shí)例方法協(xié)議實(shí)現(xiàn)
print(MethodProtocolClass.classMethod(para: "Hi!"))//!< Hi!類(lèi)方法協(xié)議實(shí)現(xiàn)
print(instance.hasVariableParameter(somePara: "QiShare"," ","Come On","!"))//!< QiShare Come On!
可變方法要求
值類(lèi)型實(shí)現(xiàn)協(xié)議方法,修改值類(lèi)型實(shí)例本身,則此協(xié)議方法需要使用mutating關(guān)鍵字作為前綴。
若定義協(xié)議實(shí)例方法,旨在對(duì)所有遵守該協(xié)議的實(shí)例進(jìn)行修改,需要將此方法標(biāo)記mutating,以涵蓋值類(lèi)型。注:mutating標(biāo)記的協(xié)議方法,由類(lèi)類(lèi)型實(shí)現(xiàn)時(shí),無(wú)需編寫(xiě)mutating關(guān)鍵字,mutating關(guān)鍵字僅由結(jié)構(gòu)和枚舉使用。
protocol Togglable {
mutating func switchOperate()
}
extension Bool : Togglable {
mutating func switchOperate() {
self = !self
}
}
//調(diào)用
var value = false
value.switchOperate()
print(value)//true
協(xié)議中定義初始化方法的要求
協(xié)議中定義初始化方法也和普通情況下定義初始化方法一樣,唯一區(qū)別便是沒(méi)有函數(shù)體和花括號(hào)。會(huì)要求遵守該協(xié)議的類(lèi)型實(shí)現(xiàn)此初始化方法。
protocol SomeProtocol {
init(someParameter: Int)
}
類(lèi),實(shí)現(xiàn)協(xié)議中初始化方法的要求
遵守協(xié)議的類(lèi)類(lèi)型可以實(shí)現(xiàn)協(xié)議中的初始化方法為指定初始化方法或者便利初始化方法,無(wú)論哪種實(shí)現(xiàn),都必須使用required關(guān)鍵字標(biāo)記。
回顧:
required關(guān)鍵字修飾初始化方法,標(biāo)記此初始化方法其所有子類(lèi)必須要實(shí)現(xiàn),子類(lèi)實(shí)現(xiàn)required標(biāo)記的初始化方法無(wú)需寫(xiě)override,但必須寫(xiě)required,指示該初始化方法的延續(xù)性。
//類(lèi),實(shí)現(xiàn)協(xié)議中初始化方法可謂是簽署了魔鬼契約,要求子子孫孫都要履行,可謂之曰霸氣側(cè)漏
class SomeClass: SomeProtocol {
required init(someParameter: Int) {
}
//或者實(shí)現(xiàn)為便利初始化方法
required convenience init(someParameter: Int) {
self.init()
}
}
使用required關(guān)鍵字確保了遵守此協(xié)議的類(lèi)類(lèi)型的所有子類(lèi)都能提供協(xié)議中初始化方法的實(shí)現(xiàn),使其子類(lèi)都遵守協(xié)議。
注意:使用final標(biāo)記類(lèi)類(lèi)型,實(shí)現(xiàn)協(xié)議初始化方法時(shí),無(wú)需使用required關(guān)鍵字,因?yàn)?code>final是阻止子類(lèi)化的。
特殊:若子類(lèi)重寫(xiě)了父類(lèi)的指定初始化方法,并且協(xié)議中的初始化方法與子類(lèi)重寫(xiě)的父類(lèi)的初始化方法一致,則子類(lèi)實(shí)現(xiàn)此協(xié)議時(shí),需同時(shí)使用required和override修飾符進(jìn)行標(biāo)記。
protocol SomeProtocol {
init()
}
class SomeSuperClass {
init() {
}
}
class SomeSubClass: SomeSuperClass, SomeProtocol {
// "required"表示遵守協(xié)議; "override" 表示重寫(xiě)
required override init() {
//初始化方法零參數(shù)符合省略super.init()條件
}
}
協(xié)議中定義可失敗的初始化方法
協(xié)議中定義的可失敗的初始化方法,可以被遵守該協(xié)議的類(lèi)型實(shí)現(xiàn)為可失敗的初始化方法或者不可失敗的初始化方法。
protocol SomeProtocol {
init?(name:String)
}
class SomeClass : SomeProtocol {
required init(name:String) {
}
required init?(name: String) {
if name.isEmpty {return nil}
}
required init!(name: String) {
}
}
作為類(lèi)型使用的協(xié)議
協(xié)議本身實(shí)際上并未實(shí)現(xiàn)任何功能。但是卻可以將協(xié)議用作完整類(lèi)型。
使用協(xié)議作為類(lèi)型有時(shí)也稱(chēng)為存在類(lèi)型,存在類(lèi)型來(lái)自短語(yǔ)“存在類(lèi)型T,使得T遵守協(xié)議”。類(lèi)似OC中的@property (nonatomic, weak) id<protocolName> delegate。
可以在允許使用其他類(lèi)型的許多地方使用協(xié)議:
- 作為函數(shù),方法,初始化方法的參數(shù)類(lèi)型或返回值類(lèi)型
- 作為常量,變量,或?qū)傩缘念?lèi)型
- 作為數(shù)組,字典或其他容器的元素類(lèi)型
protocol RandomNumberProtocol {
func random() -> UInt
}
class RandomNumberSmallGenerator: RandomNumberProtocol {
var name = "遵守協(xié)議的屬性"
func random() -> UInt {
return UInt(arc4random() % 10)
}
}
class RandomNumberBigGenerator: RandomNumberProtocol {
func random() -> UInt {
return UInt(arc4random() % 10) + 10
}
}
class BoomTest {
var generator : RandomNumberProtocol //!< 協(xié)議作為類(lèi)型修飾屬性
var description : String
//!< 協(xié)議作為類(lèi)型修飾方法的參數(shù),所有符合`RandomNumberProtocol`的類(lèi)型都可以傳入
init(des:String,random:RandomNumberProtocol) {
generator = random
print("初始化時(shí)調(diào)用一下協(xié)議方法:\(generator.random())")
description = des
}
//!<如何使用協(xié)議中的方法呢?需要單獨(dú)定義方法
func toRandom() -> String {
description + "\(generator.random())"
}
}
//調(diào)用
let randomNum = BoomTest.init(des: "??", random: RandomNumberSmallGenerator())
for _ in 0...2 {
let result = randomNum.toRandom()
print(result)
}
//輸出
??5
??4
??0
協(xié)議類(lèi)型的集合:
協(xié)議作為集合的元素類(lèi)型舉例:
let protocolArray : [RandomNumberProtocol] = [RandomNumberSmallGenerator(),RandomNumberBigGenerator()]
for item in protocolArray {
let num = item.random()
print(num) //!< 1 17
}
參數(shù)或?qū)傩詾閰f(xié)議類(lèi)型時(shí),當(dāng)傳入遵守此協(xié)議的類(lèi)型時(shí),是否可以通過(guò)此屬性或參數(shù),來(lái)訪(fǎng)問(wèn)遵守此協(xié)議類(lèi)型的方法或者屬性?答案是不能直接訪(fǎng)問(wèn),通過(guò)協(xié)議類(lèi)型的屬性或參數(shù)只能訪(fǎng)問(wèn)到協(xié)議中的方法或?qū)傩?,如何做到?需要?lèi)型轉(zhuǎn)換。
for item in protocolArray {
let num = item.random()
if let smallGenerator = item as? RandomNumberSmallGenerator {
print(smallGenerator.name) //log:遵守協(xié)議的屬性
}
print(num) //!< 1 17
}
Delegation
委托是一種設(shè)計(jì)模式,使類(lèi)或結(jié)構(gòu)體可以將其某些職責(zé)委托給其他類(lèi)型的實(shí)例。通過(guò)定義封裝委托職責(zé)的協(xié)議來(lái)實(shí)現(xiàn)此設(shè)計(jì)模式,從而保證遵守協(xié)議的類(lèi)型(或稱(chēng)委托)提供協(xié)議需要的的功能。委托可用于響應(yīng)特定操作,或從外部源檢索數(shù)據(jù),而無(wú)需了解該源的底層類(lèi)型。
這種模式OC中也是有的,示例如下:
protocol PersonActivity {
func sleep()
func eat()
func play()
}
//通過(guò)將AnyObject協(xié)議添加到協(xié)議的繼承列表中,可以將遵守此協(xié)議的類(lèi)型限制為類(lèi)類(lèi)型(而不是結(jié)構(gòu)或枚舉)。
protocol PersonDelegate : AnyObject {
func personNowDoSomething(name: String,SomeThing:String) -> Void
}
class Person: PersonActivity {
var name : String
var age : UInt
//!< 使用了weak 因此遵守此協(xié)議的類(lèi)型必須是類(lèi)類(lèi)型,故`PersonDelegate`繼承`anyobjct`
weak var delegate : PersonDelegate?
init(name:String = "QiShare",age:UInt = 1) {
self.name = name
self.age = age
}
func activity() -> Void {
let num = arc4random()%3
switch num {
case 0:
sleep()
case 1:
eat()
default:
play()
}
}
func sleep() {
delegate?.personNowDoSomething(name:name,SomeThing: "睡覺(jué)")
}
func eat() {
delegate?.personNowDoSomething(name:name,SomeThing: "吃飯")
}
func play() {
delegate?.personNowDoSomething(name:name,SomeThing: "玩耍")
}
}
class DelegationClass:PersonDelegate{
func personNowDoSomething(name:String,SomeThing: String) {
print(name + "正在" + SomeThing) //會(huì)輸出
}
}
注意:為了防止強(qiáng)引用循環(huán),需要將委托聲明為弱引用,即使用weak修飾。
僅類(lèi)類(lèi)型遵守的協(xié)議Class-Only Protocols
通過(guò)將AnyObject協(xié)議添加到協(xié)議的繼承中,可以將遵守此協(xié)議的類(lèi)型限制為類(lèi)類(lèi)型(而不是結(jié)構(gòu)或枚舉,否則會(huì)觸發(fā)編譯時(shí)錯(cuò)誤)。使用weak 修飾的協(xié)議類(lèi)型的屬性,則傳入的遵守此協(xié)議的實(shí)例只能為類(lèi)類(lèi)型實(shí)例。
使用擴(kuò)展使某個(gè)類(lèi)型遵守協(xié)議
使用擴(kuò)展使得現(xiàn)有類(lèi)型遵守新的協(xié)議。擴(kuò)展能夠?yàn)楝F(xiàn)有類(lèi)型添加計(jì)算屬性,下標(biāo),方法,因此能夠添加協(xié)議要求的方法,屬性。
注:當(dāng)協(xié)議被遵守和實(shí)現(xiàn)在實(shí)例的類(lèi)型擴(kuò)展中,現(xiàn)有類(lèi)型的實(shí)例會(huì)自動(dòng)采用和遵守。
protocol Togglable {
mutating func switchOperate()
}
extension Bool : Togglable {
mutating func switchOperate() {
self = !self
}
}
//調(diào)用
var value = false
value.switchOperate()
print(value)//true
有條件地遵守協(xié)議
泛型僅在特定的條件下能夠滿(mǎn)足協(xié)議的要求。通過(guò)協(xié)議名稱(chēng)后使用泛型的where子句來(lái)使泛型類(lèi)型有條件的遵守協(xié)議。
// Array就是泛型`Array<Element>`
extension Array : RandomNumberProtocol where Element : RandomNumberProtocol {
func random() -> UInt {
732
}
}
//調(diào)用
let num1 = RandomNumberSmallGenerator()
let num2 = RandomNumberSmallGenerator()
let protocolArray = [num1,num2]
print("\(protocolArray.random())")//!< 732
上述測(cè)試示例總結(jié):
-
extension Array : RandomNumberProtocol where Element : RandomNumberProtocol表示Array中的元素都遵守某個(gè)協(xié)議時(shí),Array的實(shí)例才能使用此協(xié)議方法。 - 若
let num2 = RandomNumberBigGenerator()則會(huì)報(bào)錯(cuò)Protocol type 'Any' cannot conform to 'RandomNumberProtocol' because only concrete types can conform to protocols。故Array必須是固定類(lèi)型,不能是Any或AnyObject,才能遵守此協(xié)議。 - 若
let protocolArray : [RandomNumberProtocol] = [num1,num2]則會(huì)報(bào)錯(cuò):Protocol type 'RandomNumberProtocol' cannot conform to 'RandomNumberProtocol' because only concrete types can conform to protocols
extension Array : RandomNumberProtocol where Element : RandomNumberProtocol
不能使用特定類(lèi)型的Array,例如:Array<String>。
通過(guò)擴(kuò)展聲明遵守協(xié)議
如果類(lèi)型已經(jīng)遵守協(xié)議的所有要求,但尚未聲明該類(lèi)型遵守協(xié)議,則可以通過(guò)一個(gè)空擴(kuò)展 來(lái)聲明。
protocol RandomNumberProtocol {
func random() -> UInt
}
class RandomNumberSmallGenerator {
var name = "遵守協(xié)議的屬性"
func random() -> UInt {
return UInt(arc4random() % 10)
}
}
extension RandomNumberSmallGenerator : RandomNumberProtocol {}
協(xié)議的繼承
協(xié)議可以繼承一個(gè)或多個(gè)其他協(xié)議,并且可以在繼承的要求(方法、屬性)之上添加其他要求。協(xié)議繼承的語(yǔ)法類(lèi)似于類(lèi)繼承的語(yǔ)法,多個(gè)繼承的協(xié)議之間逗號(hào)分隔?;菊Z(yǔ)法:
protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
}
示例如下:
protocol TextDescription {
func TextDescription()->String
}
protocol AdditionTextDescription : TextDescription {
var goodEvaluation : String {get}
}
struct Evaluation : AdditionTextDescription {
var goodEvaluation: String {
TextDescription() + "很好!"
}
func TextDescription() -> String {
"這個(gè)結(jié)構(gòu)體"
}
}
//調(diào)用
let evaluation = Evaluation.init()
print(evaluation.goodEvaluation)//!< 這個(gè)結(jié)構(gòu)體很好!
協(xié)議組合
協(xié)議組合可以組合多個(gè)協(xié)議成為一個(gè)臨時(shí)的本地協(xié)議,該協(xié)議具備了組合的所有協(xié)議要求,且不會(huì)任何新的協(xié)議類(lèi)型。這對(duì)于要求某個(gè)類(lèi)型同時(shí)遵守多個(gè)協(xié)議是很有用的。
協(xié)議組合的形式SomeProtocol & AnotherProtocol??梢允褂?code>&作為分隔符列出需要的任意數(shù)量的協(xié)議。另外,協(xié)議組合也可以包含類(lèi)類(lèi)型,包含的類(lèi)類(lèi)型,可以用來(lái)指定遵守組合協(xié)議的類(lèi)的父類(lèi),驗(yàn)證得知:本類(lèi)也是可以的。
protocol Color {
var color : String {get}
}
protocol Feature {
var feature : String {get}
}
class Dog {
var name : String
init(_ name : String = "阿里克") {
self.name = name
}
}
class Cat : Color,Feature{
var name : String
var color: String{
"黑色"
}
var feature: String{
"撒嬌"
}
init(_ name : String = "小黃") {
self.name = name
}
}
class Husky: Dog,Color,Feature {
var color: String
var feature: String
init(_ name : String,_ color : String,_ feature : String) {
self.color = color
self.feature = feature
}
}
class Kid {
static func hasCat(pet:Cat&Color&Feature)->String {
"恭喜你獲得了寵物貓:\(pet.name) 顏色:\(pet.color) 特點(diǎn):\(pet.feature)"
}
static func hasDog(pet:Dog&Color&Feature)->String {
"恭喜你獲得了寵物狗:\(pet.name) 顏色:\(pet.color) 特點(diǎn):\(pet.feature)"
}
///本類(lèi)也是可以的
///static func hasDog(pet: Husky&Color&Feature)->String {
/// "恭喜你獲得了寵物狗:\(pet.name) 顏色:\(pet.color) 特點(diǎn):\(pet.feature)"
/// }
}
//調(diào)用
let cat = Kid.hasCat(pet: Cat.init())//!< 恭喜你獲得了寵物貓:小黃 顏色:黑色 特點(diǎn):撒嬌
print(cat)
let dog = Kid.hasDog(pet: Husky.init("哈慫奇", "火紅", "家中地雷"))//!< 恭喜你獲得了寵物狗:阿里克 顏色:火紅 特點(diǎn):家中地雷
print(dog)
檢查類(lèi)型實(shí)例是否遵守特定協(xié)議
使用is和as檢查類(lèi)型實(shí)例是否遵守特定協(xié)議并且轉(zhuǎn)換為特定協(xié)議。語(yǔ)法與類(lèi)型轉(zhuǎn)換一樣。
- 如果實(shí)例遵守協(xié)議,則
is運(yùn)算符返回true,否則返回false。 -
as?向下轉(zhuǎn)換為目標(biāo)協(xié)議類(lèi)型的可選值,如果實(shí)例不遵守該協(xié)議,則返回nil。 -
as!強(qiáng)制向下轉(zhuǎn)換為目標(biāo)協(xié)議類(lèi)型,轉(zhuǎn)換失敗則是觸發(fā)運(yùn)行時(shí)錯(cuò)誤。
protocol HasArea {
var area: Double { get }
}
class Circle: HasArea {
let pi = 3.1415927
var radius: Double
var area: Double { return pi * radius * radius }
init(radius: Double) { self.radius = radius }
}
class Country: HasArea {
var area: Double
init(area: Double) { self.area = area }
}
//調(diào)用
let objects: [AnyObject] = [
Circle(radius: 2.0),
Country(area: 243_610),
Husky.init("哈士奇", "火紅", "家中地雷")
]
for object in objects {
if let objectWithArea = object as? HasArea {
print("Area is \(objectWithArea.area)")
} else {
print("Something that doesn't have an area")
}
}
可選協(xié)議要求
我們可以定義協(xié)議可選的要求,這些要求不是必須被遵守此協(xié)議的類(lèi)型實(shí)現(xiàn)的。即:我們可以編寫(xiě)遵守某個(gè)協(xié)議的自定義類(lèi),而無(wú)需實(shí)現(xiàn)任何可選協(xié)議要求。
協(xié)議的可選要求的定義:使用optional修飾符作為前綴,定義協(xié)議要求即可。
在協(xié)議的可選要求中使用方法或?qū)傩詴r(shí),其類(lèi)型將自動(dòng)變?yōu)榭蛇x。例如,類(lèi)型(Int)->String的方法變?yōu)?code>((Int)->String)?。注意:是整個(gè)函數(shù)類(lèi)型都包裝在可選內(nèi)容中,而不是方法的返回值中。本質(zhì)是:協(xié)議中定義的方法名稱(chēng)便是函數(shù)的實(shí)例,此函數(shù)類(lèi)型變?yōu)榱丝蛇x。
protocol CounterDataSource {
optional func increment(forCount count: Int) -> Int
optional var fixedIncrement: Int { get }
}
實(shí)際操作過(guò)程中發(fā)現(xiàn)不使用@objc修飾協(xié)議,是無(wú)法使用optional修飾符的,否則報(bào)錯(cuò):'optional' requirements are an Objective-C compatibility feature。
總結(jié):通過(guò)這個(gè)錯(cuò)誤我們才知道原來(lái),可選協(xié)議要求是Objective-C兼容性功能,optional必須與@objc聯(lián)合使用:
-
optional必須在@objc修飾的協(xié)議下使用,并且必須采用@objc optional的形式。 - 協(xié)議的可選要求不能被結(jié)構(gòu)體或枚舉采用。
使用舉例:
@objc protocol CounterDataSource {
@objc optional func increment(forCount count: Int) -> Int
@objc optional var fixedIncrement: Int { get }
}
class Counter {
var count = 0
var dataSource: CounterDataSource?
func increment() {
///注意:因?yàn)閛ptional, 函數(shù)類(lèi)型實(shí)例increment自動(dòng)變?yōu)榭蛇x項(xiàng),屬性也是一樣的,
if let amount = dataSource?.increment?(forCount: count) {
count += amount
} else if let amount = dataSource?.fixedIncrement {
count += amount
}
}
}
class ThreeSource: CounterDataSource {
let fixedIncrement: Int = 3
}
//調(diào)用
let counter = Counter()
counter.dataSource = ThreeSource()
for _ in 1...4 {
counter.increment()
print(counter.count) // 3 6 9 12
}
協(xié)議擴(kuò)展
協(xié)議通過(guò)擴(kuò)展可以為遵守協(xié)議的類(lèi)型提供方法,初始化,下標(biāo)和計(jì)算屬性的實(shí)現(xiàn)。這一點(diǎn)允許我們?yōu)閰f(xié)議本身定義行為,而不是基于遵守協(xié)議的每個(gè)類(lèi)型。
協(xié)議擴(kuò)展可以將實(shí)現(xiàn)添加到遵守協(xié)議的類(lèi)型中,但不能使協(xié)議要求進(jìn)行擴(kuò)展或從另一個(gè)協(xié)議繼承。協(xié)議繼承始終在協(xié)議聲明本身中指定。
protocol Color {
var color : String {get}
}
protocol Feature {
var feature : String {get}
}
//擴(kuò)展協(xié)議Feature
extension Feature {
var like_feature : String {
return "喜歡" + feature
}
}
class Kid {
static func hasCat(pet:Cat&Color&Feature)->String {
"恭喜你獲得了寵物貓,特點(diǎn):\(pet.like_feature)"
}
static func hasDog(pet:Dog&Color&Feature)->String {
"恭喜你獲得了寵物狗,特點(diǎn):\(pet.like_feature)"
}
}
//調(diào)用結(jié)果
恭喜你獲得了寵物貓,特點(diǎn):喜歡撒嬌
恭喜你獲得了寵物狗, 特點(diǎn):喜歡搗蛋
協(xié)議要求提供默認(rèn)實(shí)現(xiàn)
我們可以使用協(xié)議擴(kuò)展為當(dāng)前協(xié)議要求定義的任何方法或計(jì)算屬性提供默認(rèn)的實(shí)現(xiàn)。如果一個(gè)遵守此協(xié)議的類(lèi)型提供了屬于它自己關(guān)于某個(gè)協(xié)議要求的實(shí)現(xiàn),那么將會(huì)代替協(xié)議擴(kuò)展中提供的那一個(gè)。
通過(guò)擴(kuò)展提供協(xié)議的默認(rèn)實(shí)現(xiàn),也可以使得遵守該協(xié)議的類(lèi)型不必提供它們自己的實(shí)現(xiàn),這點(diǎn)和可選協(xié)議要求一樣。但是采用這種方式為可選協(xié)議要求增加默認(rèn)實(shí)現(xiàn)后,則無(wú)需使用可選鏈接。
protocol Color {
var color : String {get}
}
protocol Feature {
var feature : String {get}
}
//為這兩個(gè)協(xié)議提供默認(rèn)實(shí)現(xiàn)
extension Color {
var color : String {
"彩虹色"
}
// var feature : String {
// "動(dòng)物能有啥子特點(diǎn)!"
// }
}
extension Feature {
var feature : String {
"動(dòng)物能有啥子特點(diǎn)?"
}
}
class Cat : Color,Feature{
var name : String
var color: String{
"黑色"
}
// var feature: String{
// "撒嬌"
// }
init(_ name : String = "小黃") {
self.name = name
}
}
class Kid {
static func hasCat(pet:Cat&Color&Feature)->String {
"恭喜你獲得了寵物貓:\(pet.name) 顏色:\(pet.color) 特點(diǎn):\(pet.like_feature)"
}
}
//log:恭喜你獲得了寵物貓:小黃 顏色:黑色 特點(diǎn):喜歡動(dòng)物能有啥子特點(diǎn)?
添加約束到協(xié)議擴(kuò)展
定義協(xié)議擴(kuò)展時(shí),在協(xié)議擴(kuò)展中定義的方法與屬性可用之前,我們可以指定遵守此協(xié)議的類(lèi)型必須滿(mǎn)足的約束條件,否則將不可用。
方式:在要擴(kuò)展的協(xié)議名稱(chēng)使用泛型where子句添加約束。
extension Collection where Element: Equatable {
func allEqual() -> Bool {
for element in self {
if element != self.first {
return false
}
}
return true
}
}
let equalNumbers = [100, 100, 100, 100, 100]
let differentNumbers = [100, 100, 200, 100, 200]
//let differentNumbers : [Cat] = [Cat.init(), Cat.init()]
//觸發(fā)錯(cuò)誤:Referencing instance method 'allEqual()' on 'Collection' requires that 'Cat' conform to 'Equatable'
print(equalNumbers.allEqual())//true
print(differentNumbers.allEqual()) //false
參考資料:
swift 5.1官方編程指南