協(xié)議語法
- 與類呻澜、結(jié)構(gòu)體递礼、枚舉類型非常相似
protocol SomeProtocol {
// protocol definition goes here
}
- 表示該類型采納協(xié)議,多個(gè)協(xié)議用逗號分開:
```swift
struct SomeStructure: FirstProtocol, AnotherProtocol {
// structure definition goes here
}
- 有父類的寫法羹幸,父類名放協(xié)議名之前脊髓,用逗號分隔
class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol {
// class definition goes here
}
屬性要求
- 場景:要求遵循該協(xié)議的類型,提供特定名字和類型的實(shí)例屬性或類型屬性
- 特性:
- 不要求 存儲(chǔ)屬性 or 計(jì)算屬性(只要求名稱和類型一致)
- 須明確可讀可寫(set 和 get)
- 可讀寫:不能用常量栅受、只讀屬性
- 只讀:任意類型
protocol SomeProtocol {
var mustBeSettable: Int { get set }
var doesNotNeedToBeSettable: Int { get }
}
- 類型屬性: 實(shí)現(xiàn)時(shí)将硝,使用 class 或static 關(guān)鍵字
protocol AnotherProtocol {
static var someTypeProperty: Int { get set }
}
- 只有一個(gè)實(shí)例屬性要求
protocol FullyNamed {
var fullName: String { get }
}
- 使用
struct Person: FullyNamed {
var fullName: String
}
let john = Person(fullName: "John Appleseed")
// john.fullName is "John Appleseed"
- 更加復(fù)雜的類,遵循 FullyNamed 協(xié)議
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 ncc1701 = Starship(name: "Enterprise", prefix: "USS")
// ncc1701.fullName is "USS Enterprise"
- 當(dāng) prefix 值存在時(shí)屏镊,fullName 將 prefix 放在 name 之前以創(chuàng)建星艦的全名
方法要求
- 使用場景:要求采納的類型依疼,實(shí)現(xiàn)指定的實(shí)例方法和類方法
- 語法:
- 與正常實(shí)例、類方法相同而芥,但不需大括號
- 方法參數(shù)不能定義默認(rèn)值(可在擴(kuò)展中寫默認(rèn)實(shí)現(xiàn))
protocol SomeProtocol {
static func someTypeMethod()
}
- 只有一個(gè)實(shí)例方法要求的協(xié)議律罢,要求生成器提供一個(gè)生成隨機(jī)數(shù)的標(biāo)準(zhǔn)過程
protocol RandomNumberGenerator {
func random() -> Double
}
class LinearCongruentialGenerator: RandomNumberGenerator {
var lastRandom = 42.0
let m = 139968.0
let a = 3877.0
let c = 29573.0
func random() -> Double {
lastRandom = ((lastRandom * a + c).truncatingRemainder(dividingBy:m))
return lastRandom / m
}
}
let generator = LinearCongruentialGenerator()
print("Here's a random number: \(generator.random())")
// Prints "Here's a random number: 0.37464991998171"
print("And another one: \(generator.random())")
// Prints "And another one: 0.729023776863283"
異變方法要求
- 場景:方法需要改變(或異變)其所屬的實(shí)例
- 語法:在方法的 func 關(guān)鍵字之前,使用 mutating 關(guān)鍵字
在協(xié)議標(biāo)記實(shí)例方法 mutating 棍丐,mutating 只在結(jié)構(gòu)體和枚舉要寫误辑,為類實(shí)現(xiàn)該方法的時(shí)不用寫 mutating
protocol Togglable {
mutating func toggle()
}
enum OnOffSwitch: Togglable {
case off, on
mutating func toggle() {
switch self {
case .off:
self = .on
case .on:
self = .off
}
}
}
var lightSwitch = OnOffSwitch.off
lightSwitch.toggle()
// lightSwitch is now equal to .on
構(gòu)造器要求
場景:遵循協(xié)議的類型實(shí)現(xiàn)指定的初始化器
語法:不用寫大括號
protocol SomeProtocol {
init(someParameter: Int)
}
協(xié)議構(gòu)造器要求的類實(shí)現(xiàn)
- 實(shí)現(xiàn)要求:
- 指定和便捷初始化器都可以
- 必須用 required 關(guān)鍵字修飾(保證了子類繼承父類協(xié)議時(shí),實(shí)現(xiàn)方法)
class SomeClass: SomeProtocol {
required init(someParameter: Int) {
// initializer implementation goes here
}
}
由于 final 的類沒子類歌逢,協(xié)議初始化器實(shí)現(xiàn)的類巾钉,用 final 標(biāo)記,不需用 required 修飾秘案。因?yàn)檫@樣的類不能被繼承子類砰苍。詳見 阻止重寫
- 一個(gè)子類潦匈,重寫了父類指定的初始化器 + 遵循協(xié)議實(shí)現(xiàn)了要求的初始化器,這個(gè)初始化器的實(shí)現(xiàn)添加 required 和 override
protocol SomeProtocol {
init()
}
class SomeSuperClass {
init() {
// initializer implementation goes here
}
}
class SomeSubClass: SomeSuperClass, SomeProtocol {
// "required" from SomeProtocol conformance; "override" from SomeSuperClass
required override init() {
// initializer implementation goes here
}
}
可失敗構(gòu)造器要求
- 可失敗的初始化器的實(shí)現(xiàn):
- 不可失敗初始化器
- 隱式展開的可失敗初始化器
協(xié)議作為類型
- 協(xié)議自身并不實(shí)現(xiàn)功能赚导,可以變?yōu)橐粋€(gè)功能完備的類型在代碼中使用历等。
- 作為參數(shù)或返回類型
- 作為常量、變量或者屬性的類型辟癌;
- 作為數(shù)組寒屯、字典或者其他存儲(chǔ)器的元素的類型
協(xié)議是類型,要開頭大寫(比如說 FullyNamed 和 RandomNumberGenerator )來匹配 Swift 里其他類型名稱格式(比如說 Int 黍少、 String 還有 Double )
- 例子:
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
}
}
generator 屬性是 RandomNumberGenerator 類型寡夹,任何采納了RandomNumberGenerator 協(xié)議的類型
初始化器有一個(gè)形式參數(shù)叫做generator ,它同樣也是 RandomNumberGenerator 類型厂置,接收遵循這個(gè)協(xié)議的類型的值
random() 方法菩掏,由于 generator 已知采納了RandomNumberGenerator ,它保證了會(huì)有 random() 方法以供調(diào)用
- Dice 類用 LinearCongurentialGenerator 實(shí)例創(chuàng)建一個(gè)六面骰子的隨機(jī)數(shù)生成器昵济,來創(chuàng)建一個(gè)六面骰子
var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator())
for _ in 1...5 {
print("Random dice roll is \(d6.roll())")
}
委托 / 代理
- 是一種設(shè)計(jì)模式:委托/代理模式
- 通過定義一個(gè)封裝了委托責(zé)任的協(xié)議來實(shí)現(xiàn)
- 遵循了協(xié)議的類型(所謂的委托)來保證提供被委托的功能
- 響應(yīng)一個(gè)特定的行為
- 從外部資源取回?cái)?shù)據(jù)(而不需要了解資源具體的類型)
Delegation 委托智绸,可能也以“代理”而為人熟知,這里我們選擇譯為“委托”是為了更好的理解避免混淆
- 定義了兩個(gè)協(xié)議以用于基于骰子的棋盤游戲:
protocol DiceGame {
var dice: Dice { get }
func play()
}
protocol DiceGameDelegate {
func gameDidStart(_ game: DiceGame)
func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int)
func gameDidEnd(_ game: DiceGame)
}
DiceGame 與骰子有關(guān)的游戲采納的協(xié)議访忿,DiceGameDelegate 協(xié)議可以被任何追蹤 DiceGame 進(jìn)度的類型采納
采用 DiceGame 協(xié)議瞧栗;然后通知一個(gè) DiceGameDelegate 關(guān)于進(jìn)度的信息
class SnakesAndLadders: DiceGame {
let finalSquare = 25
let dice = Dice(sides: 6, generator: LinearCongruentialGenerator())
var square = 0
var board: [Int]
init() {
board = Array(repeating: 0, count: finalSquare + 1)
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
}
var delegate: DiceGameDelegate?
func play() {
square = 0
delegate?.gameDidStart(self)
gameLoop: while square != finalSquare {
let diceRoll = dice.roll()
delegate?.game(self, didStartNewTurnWithDiceRoll: diceRoll)
switch square + diceRoll {
case finalSquare:
break gameLoop
case let newSquare where newSquare > finalSquare:
continue gameLoop
default:
square += diceRoll
square += board[square]
}
}
delegate?.gameDidEnd(self)
}
}
dice 屬性為常量,因它不需初始化后再改變海铆,而且協(xié)議只需它是可讀
delegate 屬性為可選 迹恐,自動(dòng)初始化為 nil ,play() 方法調(diào)用委托時(shí)候需用可選鏈
DiceGameDelegate 協(xié)議:
class DiceGameTracker: DiceGameDelegate {
var numberOfTurns = 0
func gameDidStart(_ game: DiceGame) {
numberOfTurns = 0
if game is SnakesAndLadders {
print("Started a new game of Snakes and Ladders")
}
print("The game is using a \(game.dice.sides)-sided dice")
}
func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) {
numberOfTurns += 1
print("Rolled a \(diceRoll)")
}
func gameDidEnd(_ game: DiceGame) {
print("The game lasted for \(numberOfTurns) turns")
}
}
let tracker = DiceGameTracker()
let game = SnakesAndLadders()
game.delegate = tracker
game.play()
// Started a new game of Snakes and Ladders
// The game is using a 6-sided dice
// Rolled a 3
// Rolled a 5
// Rolled a 4
// Rolled a 5
// The game lasted for 4 turns
在擴(kuò)展里添加協(xié)議遵循
- 場景:給已存在的類遵循一個(gè)新協(xié)議(無法訪問該類型的源代碼也行)
類型已經(jīng)存在的實(shí)例卧斟,自動(dòng)地采納和遵循這個(gè)協(xié)議殴边。
- 用文本表達(dá)的類型實(shí)現(xiàn)
protocol TextRepresentable {
var textualDescription: String { get }
}
- 遵循 TextRepresentable :
extension Dice: TextRepresentable {
var textualDescription: String {
return "A \(sides)-sided dice"
}
}
- Dice 實(shí)例現(xiàn)在都可以被視作 TextRepresentable :
let d12 = Dice(sides: 12, generator: LinearCongruentialGenerator())
print(d12.textualDescription)
// Prints "A 12-sided dice"
extension SnakesAndLadders: TextRepresentable {
var textualDescription: String {
return "A game of Snakes and Ladders with \(finalSquare) squares"
}
}
print(game.textualDescription)
有條件地遵循協(xié)議
- 使用場景:泛型有條件遵守協(xié)議
- 語法:協(xié)議的名字后面寫泛型 where 分句
- 讓 Array 類型在存儲(chǔ)遵循 TextRepresentable 協(xié)議的元素時(shí)遵循TextRepresentable 協(xié)議
- 讓元素?fù)碛袇f(xié)議方法
extension Array: TextRepresentable where Element: TextRepresentable {
var textualDescription: String {
let itemsAsText = self.map { $0.textualDescription }
return "[" + itemsAsText.joined(separator: ", ") + "]"
}
}
let myDice = [d6, d12]
print(myDice.textualDescription)
// Prints "[A 6-sided dice, A 12-sided dice]"
在擴(kuò)展里聲明采納協(xié)議
- 場景:實(shí)現(xiàn)了協(xié)議方法,但沒遵循協(xié)議
- 語法:遵循協(xié)議 + 空拓展
struct Hamster {
var name: String
var textualDescription: String {
return "A hamster named \(name)"
}
}
extension Hamster: TextRepresentable {}
- Hamster 實(shí)例珍语,可以賦值給遵守 TextRepresentable 協(xié)議的實(shí)例
let simonTheHamster = Hamster(name: "Simon")
let somethingTextRepresentable: TextRepresentable = simonTheHamster
print(somethingTextRepresentable.textualDescription)
// Prints "A hamster named Simon"
類型不會(huì)因?yàn)閷?shí)現(xiàn)協(xié)議方法锤岸,就自動(dòng)遵守協(xié)議,必須顯式地聲明
使用合成實(shí)現(xiàn)來采納協(xié)議
場景:使用系統(tǒng)定義 + 實(shí)現(xiàn)好的協(xié)議板乙,直接調(diào)用協(xié)議的功能方法(減少重復(fù)代碼)
常見系統(tǒng)協(xié)議: Equatable 是偷、 Hashable 以及 Comparable 協(xié)議
-
Swift 為以下自定義類型提供了 Equatable 的綜合實(shí)現(xiàn):
- 只包含遵循 Equatable 協(xié)議的存儲(chǔ)屬性的結(jié)構(gòu)體;
- 只關(guān)聯(lián)遵循 Equatable 協(xié)議的類型的枚舉亡驰;
- 沒有關(guān)聯(lián)類型的枚舉晓猛。
-
遵循 Equatable 協(xié)議
- 不需手動(dòng)實(shí)現(xiàn) == 運(yùn)算符
- 默認(rèn)的 != 實(shí)現(xiàn)
-
Swift為以下自定義類型提供了 Hashable 的綜合實(shí)現(xiàn):
- 只包含遵循 Hashable 協(xié)議的存儲(chǔ)屬性的結(jié)構(gòu)體;
- 只關(guān)聯(lián)遵循 Hashable 協(xié)議的類型的枚舉凡辱;
- 沒有關(guān)聯(lián)類型的枚舉。
-
遵循 Hashable 協(xié)議
- 不需要手動(dòng)實(shí)現(xiàn) hash(into:) 方法
- Swift為不包含原始值的枚舉栗恩,提供 Comparable 的綜合實(shí)現(xiàn)
- 如果枚舉擁有關(guān)聯(lián)類型透乾,這些類型必須都遵循 Comparable 協(xié)議
- 遵循 Comparable 協(xié)議
- 不需要手動(dòng)實(shí)現(xiàn) <= 、 > 和 >= 運(yùn)算符
定義了一個(gè)包含 beginners、intermediates以及 experts 情況的枚舉SkillLevel
Experts 還額外使用數(shù)字來記錄他們擁有的星星數(shù)量等級乳乌。
enum SkillLevel: Comparable {
case beginner
case intermediate
case expert(stars: Int)
}
var levels = [SkillLevel.intermediate, SkillLevel.beginner,
SkillLevel.expert(stars: 5), SkillLevel.expert(stars: 3)]
for level in levels.sorted() {
print(level)
}
// Prints "beginner"
// Prints "intermediate"
// Prints "expert(stars: 3)"
// Prints "expert(stars: 5)"
協(xié)議類型的集合
- 場景:存儲(chǔ)遵守統(tǒng)一協(xié)議的元素
let things: [TextRepresentable] = [game, d12, simonTheHamster]
- 現(xiàn)在可以遍歷數(shù)組中的元素了捧韵,并且打印每一個(gè)元素的文本化描述:
for thing in things {
print(thing.textualDescription)
}
// A game of Snakes and Ladders with 25 squares
// A 12-sided dice
// A hamster named Simon
- 注意 thing 常量是 TextRepresentable 類型。它不是 Dice 類型汉操,抑或 DiceGame 還是Hamster 再来,就算后臺(tái)實(shí)際類型是它們之一
協(xié)議的繼承
- 場景:擴(kuò)充功能
- 語法:與類繼承類似,但可以多繼承
protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
// protocol definition goes here
}
- 繼承了上邊 TextRepresentable 協(xié)議
protocol PrettyTextRepresentable: TextRepresentable {
var prettyTextualDescription: String { get }
}
- SnakesAndLadders 類可以通過擴(kuò)展來采納和遵循 PrettyTextRepresentable :
extension SnakesAndLadders: PrettyTextRepresentable {
var prettyTextualDescription: String {
var output = textualDescription + ":\n"
for index in 1...finalSquare {
switch board[index] {
case let ladder where ladder > 0:
output += "▲ "
case let snake where snake < 0:
output += "▼ "
default:
output += "○ "
}
}
return output
}
}
print(game.prettyTextualDescription)
// A game of Snakes and Ladders with 25 squares:
// ○ ○ ▲ ○ ○ ▲ ○ ○ ▲ ▲ ○ ○ ○ ▼ ○ ○ ○ ○ ▼ ○ ○ ▼ ○ ▼ ○
類專屬的協(xié)議
- 場景:限制協(xié)議只能被類類型采納(并且不是結(jié)構(gòu)體或者枚舉)
- 語法:添加 AnyObject 關(guān)鍵字到協(xié)議的繼承列表
protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol {
// class-only protocol definition goes here
}
協(xié)議合成
場景:要求一個(gè)類型一次遵循多個(gè)協(xié)議
注意:協(xié)議組合不定義任何新的協(xié)議類型
語法:用 SomeProtocol & AnotherProtocol 的形式
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: "Malcolm", age: 21)
wishHappyBirthday(to: birthdayPerson)
// Prints "Happy birthday, Malcolm, you're 21!"
- 不關(guān)心具體是什么樣的類型傳入函數(shù)磷瘤,只要它遵循這兩個(gè)要求的協(xié)議即可
class Location {
var latitude: Double
var longitude: Double
init(latitude: Double, longitude: Double) {
self.latitude = latitude
self.longitude = longitude
}
}
class City: Location, Named {
var name: String
init(name: String, latitude: Double, longitude: Double) {
self.name = name
super.init(latitude: latitude, longitude: longitude)
}
}
// 任何 Location 的子類且遵循 Named 協(xié)議的類型
func beginConcert(in location: Location & Named) {
print("Hello, \(location.name)!")
}
let seattle = City(name: "Seattle", latitude: 47.6, longitude: -122.3)
beginConcert(in: seattle)
// Prints "Hello, Seattle!"
檢查協(xié)議一致性
- 場景:檢查是否遵守協(xié)議
- 語法:使用 is 和 as
- 循協(xié)議is運(yùn)算符返回 true 否則返回 false
- as? 成功芒篷,返回協(xié)議的可選項(xiàng),如果不遵循協(xié)議采缚,返回nil 针炉;
- as! 成功返回解包值,失敗運(yùn)行時(shí)報(bào)錯(cuò)
protocol HasArea {
var area: Double { get }
}
- Circle 和 Country 扳抽,這兩個(gè)類都遵循 HasArea 協(xié)議
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 }
}
- Animal 的類篡帕,它不遵循 HasArea 協(xié)議
class Animal {
var legs: Int
init(legs: Int) { self.legs = legs }
}
- Circle 、 Country 和 Animal 類并不基于相同的基類贸呢。不過它們都是類
let objects: [AnyObject] = [
Circle(radius: 2.0),
Country(area: 243_610),
Animal(legs: 4)
]
for object in objects {
if let objectWithArea = object as? HasArea {
print("Area is \(objectWithArea.area)")
} else {
print("Something that doesn't have an area")
}
}
// Area is 12.5663708
// Area is 243610.0
// Something that doesn't have an area
可選的協(xié)議要求
- 場景:不需要遵循協(xié)議的類型實(shí)現(xiàn)
- 語法:使用optional 修飾符作為前綴
- 可選要求協(xié)議被 Objective-C 遵循
- 協(xié)議和可選要求必須用 @objc 標(biāo)志標(biāo)記
- @objc 協(xié)議只能被繼承 OC 類或 @objc 類采納(不能被結(jié)構(gòu)體或者枚舉采納)
- 可選方法或?qū)傩詴r(shí)镰烧,類型自動(dòng)變成可選項(xiàng)
- 一個(gè) (Int) ->String 類型的方法會(huì)變成 ((Int) -> String)?(函數(shù)類型可選項(xiàng),不是返回值)
- 可選協(xié)議可在可選鏈中調(diào)用
- 在調(diào)用方法的時(shí)候在方法名后邊寫一個(gè)問號來檢查它是否被實(shí)現(xiàn)
someOptionalMethod?(someArgument)
- Counter 的整數(shù)計(jì)數(shù)的類楞陷,用一個(gè)外部數(shù)據(jù)源來提供它的增量
@objc protocol CounterDataSource {
@objc optional func increment(forCount count: Int) -> Int
@objc optional var fixedIncrement: Int { get }
}
可以寫一個(gè)遵循 CounterDataSource 的自定義類而不實(shí)現(xiàn)任何協(xié)議要求拌滋。反正它們都是可選的。盡管技術(shù)上來講是可以的猜谚,但這樣的話就不能做一個(gè)好的數(shù)據(jù)源了败砂。
- 定義的 Counter 類,有一個(gè)可選的 dataSource 屬性魏铅,類型是CounterDataSource?
class Counter {
var count = 0
var dataSource: CounterDataSource?
func increment() {
if let amount = dataSource?.increment?(forCount: count) {
count += amount
} else if let amount = dataSource?.fixedIncrement {
count += amount
}
}
}
dataSource 不是 nil 的時(shí)候才能調(diào)用 incrementForCount(forCount:)昌犹, dataSource 確實(shí)存在,也沒有人能保證它實(shí)現(xiàn)了 incrementForCount(forCount:) 览芳,因?yàn)樗强蛇x要求
就算 dataSource 確實(shí)存在斜姥,也沒有人能保證它實(shí)現(xiàn)了 incrementForCount(forCount:) ,因?yàn)樗强蛇x要求
據(jù)源在每次查詢時(shí)返回固定值 3 .它通過實(shí)現(xiàn)可選 fixedIncrement 屬性要求來實(shí)現(xiàn)這一點(diǎn)
class ThreeSource: NSObject, CounterDataSource {
let fixedIncrement = 3
}
- 使用 ThreeSource 的實(shí)例作為新 Counter 實(shí)例的數(shù)據(jù)源:
var counter = Counter()
counter.dataSource = ThreeSource()
for _ in 1...4 {
counter.increment()
print(counter.count)
}
// 3
// 6
// 9
// 12
- 使 Counter 實(shí)例依照它當(dāng)前的count 值往上或往下朝著零計(jì)數(shù)
@objc class TowardsZeroSource: NSObject, CounterDataSource {
func increment(forCount count: Int) -> Int {
if count == 0 {
return 0
} else if count < 0 {
return 1
} else {
return -1
}
}
}
- 使用 TowardsZeroSource 給現(xiàn)存的 Counter 實(shí)例來從 -4 到零沧竟。一旦計(jì)數(shù)器到零铸敏,就不會(huì)再變化:
counter.count = -4
counter.dataSource = TowardsZeroSource()
for _ in 1...5 {
counter.increment()
print(counter.count)
}
// -3
// -2
// -1
// 0
// 0
協(xié)議擴(kuò)展
- 場景:通過擴(kuò)展,實(shí)現(xiàn)協(xié)議屬性或方法
- RandomNumberGenerator 協(xié)議可以擴(kuò)展來提供 randomBool() 方法
extension RandomNumberGenerator {
func randomBool() -> Bool {
return random() > 0.5
}
}
- 有的遵循類型自動(dòng)獲得這個(gè)方法的實(shí)現(xiàn)
let generator = LinearCongruentialGenerator()
print("Here's a random number: \(generator.random())")
// Prints "Here's a random number: 0.37464991998171"
print("And here's a random Boolean: \(generator.randomBool())")
// Prints "And here's a random Boolean: true"
提供默認(rèn)實(shí)現(xiàn)
- 使用協(xié)議擴(kuò)展悟泵,給協(xié)議方法或者計(jì)算屬性杈笔,提供默認(rèn)實(shí)現(xiàn)
- 遵守協(xié)議 + 自定義了實(shí)現(xiàn) = 替代默認(rèn)實(shí)現(xiàn)
有默認(rèn)實(shí)現(xiàn)的要求,不需要使用可選鏈就能調(diào)用
- 提供一個(gè)默認(rèn)實(shí)現(xiàn)來簡單的返回訪問textualDescription 屬性的結(jié)果
extension PrettyTextRepresentable {
var prettyTextualDescription: String {
return textualDescription
}
}
為協(xié)議擴(kuò)展添加限制條件
- 場景:協(xié)議擴(kuò)展糕非,明確遵循類型的限制
- 語法:在擴(kuò)展協(xié)議名字后邊使用where 分句來寫這些限制
- 給 Collection 定義一個(gè)擴(kuò)展蒙具,任意元素遵循上面 TextRepresentable 協(xié)議
extension Collection where Iterator.Element: TextRepresentable {
var textualDescription: String {
let itemsAsText = self.map { $0.textualDescription }
return "[" + itemsAsText.joined(separator: ", ") + "]"
}
}
- 之前的 Hamster 結(jié)構(gòu)體球榆,它遵循 TextRepresentable 協(xié)議, Hamster 值的數(shù)組
let murrayTheHamster = Hamster(name: "Murray")
let morganTheHamster = Hamster(name: "Morgan")
let mauriceTheHamster = Hamster(name: "Maurice")
let hamsters = [murrayTheHamster, morganTheHamster, mauriceTheHamster]
- Array 遵循 Collection 并且數(shù)組的元素遵循 TextRepresentable 協(xié)議禁筏,數(shù)組可以使用 textualDescription 屬性來獲取它內(nèi)容的文本化
print(hamsters.textualDescription)
// Prints "[A hamster named Murray, A hamster named Morgan, A hamster named Maurice]"
如果遵循類型滿足了為相同方法或者屬性提供實(shí)現(xiàn)的多限制擴(kuò)展的要求持钉,Swift 會(huì)使用最匹配限制的實(shí)現(xiàn)。