翻譯:geek5nan
校對:dabing1022
本頁包含內容:
- 協議的語法(Protocol Syntax)
- 對屬性的規定(Property Requirements)
- 對方法的規定(Method Requirements)
- 對突變方法的的規定(Mutating Method Requirements)
- 協議類型(Protocols as Types)
- 委託(代理)模式(Delegation)
- 在擴展中添加協議成員(Adding Protocol Conformance with an Extension)
- 通過擴展補充協議聲明(Declaring Protocol Adoption with an Extension)
- 集合中的協議類型(Collections of Protocol Types)
- 協議的繼承(Protocol Inheritance)
- 協議合成(Protocol Composition)
- 檢驗協議的一致性(Checking for Protocol Conformance)
- 對可選協議的規定(Optional Protocol Requirements)
協議(Protocol)
用於定義完成某項任務或功能所必須的方法和屬性,協議實際上並不提供這些功能或任務的具體實現(Implementation)
--而只用來描述這些實現應該是什麼樣的。類,結構體,枚舉通過提供協議所要求的方法,屬性的具體實現來採用(adopt)
協議。任意能夠滿足協議要求的類型被稱為協議的遵循者
。
協議
可以要求其遵循者
提供特定的實例屬性,實例方法,類方法,操作符或下標(subscripts)
等。
協議
的定義方式與類,結構體,枚舉
的定義都非常相似,如下所示:
protocol SomeProtocol {
// 協議內容
}
在類型名稱後加上協議名稱
,中間以冒號:
分隔即可實現協議;實現多個協議時,各協議之間用逗號,
分隔,如下所示:
struct SomeStructure: FirstProtocol, AnotherProtocol {
// 結構體內容
}
如果一個類在含有父類
的同時也採用了協議,應當把父類
放在所有的協議
之前,如下所示:
class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
// 類的內容
}
協議可以規定其遵循者
提供特定名稱與類型的實例屬性(instance property)
或類屬性(type property)
,而不管其是存儲型屬性(stored property)
還是計算型屬性(calculate property)
。此外也可以指定屬性是只讀的還是可讀寫的。
如果協議要求屬性是可讀寫的,那麼這個屬性不能是常量存儲型屬性
或只讀計算型屬性
;如果協議要求屬性是只讀的(gettable),那麼計算型屬性
或存儲型屬性
都能滿足協議對屬性的規定,在你的代碼中,即使為只讀屬性實現了寫方法(settable)也依然有效。
協議中的屬性經常被加以var
前綴聲明其為變量屬性,在聲明後加上{ set get }
來表示屬性是可讀寫的,只讀的屬性則寫作{ get }
,如下所示:
protocol SomeProtocol {
var musBeSettable : Int { get set }
var doesNotNeedToBeSettable: Int { get }
}
如下所示,通常在協議的定義中使用class
前綴表示該屬性為類成員;在枚舉和結構體實現協議時中,需要使用static
關鍵字作為前綴。
protocol AnotherProtocol {
class var someTypeProperty: Int { get set }
}
如下所示,這是一個含有一個實例屬性要求的協議:
protocol FullyNamed {
var fullName: String { get }
}
FullyNamed
協議定義了任何擁有fullName
的類型。它並不指定具體類型,而只是要求類型必須提供一個fullName
。任何FullyNamed
類型都得有一個只讀的fullName
屬性,類型為String
。
如下所示,這是一個實現了FullyNamed
協議的簡單結構體:
struct Person: FullyNamed{
var fullName: String
}
let john = Person(fullName: "John Appleseed")
//john.fullName 為 "John Appleseed"
這個例子中定義了一個叫做Person
的結構體,用來表示具有指定名字的人。從第一行代碼中可以看出,它採用了FullyNamed
協議。
Person
結構體的每一個實例都有一個叫做fullName
,String
類型的存儲型屬性,這正好匹配了FullyNamed
協議的要求,也就意味著,Person
結構體完整的遵循
了協議。(如果協議要求未被完全滿足,在編譯時會報錯)
這有一個更為複雜的類,它採用並實現了FullyNamed
協議,如下所示:
class Starship: FullyNamed {
var prefix: String?
var name: String
init(name: String, prefix: String? = nil ) {
self.anme = name
self.prefix = prefix
}
var fullName: String {
return (prefix ? prefix ! + " " : " ") + name
}
}
var ncc1701 = Starship(name: "Enterprise", prefix: "USS")
// ncc1701.fullName == "USS Enterprise"
Starship
類把fullName
屬性實現為只讀的計算型屬性
。每一個Starship
類的實例都有一個名為name
的必備屬性和一個名為prefix
的可選屬性。 當prefix
存在時,將prefix
插入到name
之前來為Starship
構建fullName
,prefix
不存在時,則將直接用name
構建fullName
協議
可以要求其遵循者
實現某些指定的實例方法
或類方法
。這些方法作為協議的一部分,像普通的方法一樣清晰的放在協議的定義中,而不需要大括號和方法體。
注意: 協議中的方法支持
變長參數(variadic parameter)
,不支持參數默認值(default value)
。
如下所示,協議中類方法的定義與類屬性的定義相似,在協議定義的方法前置class
關鍵字來表示。當在枚舉
或結構體
實現類方法時,需要使用static
關鍵字來代替。
protocol SomeProtocol {
class func someTypeMethod()
}
如下所示,定義了含有一個實例方法的的協議。
protocol RandomNumberGenerator {
func random() -> Double
}
RandomNumberGenerator
協議要求其遵循者
必須擁有一個名為random
, 返回值類型為Double
的實例方法。 (儘管這裡並未指明,但是我們假設返回值在[0,1]區間內)。
RandomNumberGenerator
協議並不在意每一個隨機數是怎樣生成的,它只強調這裡有一個隨機數生成器。
如下所示,下邊的是一個遵循了RandomNumberGenerator
協議的類。該類實現了一個叫做*線性同餘生成器(linear congruential generator)*的偽隨機數算法。
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) % m)
return lastRandom / m
}
}
let generator = LinearCongruentialGenerator()
println("Here's a random number: \(generator.random())")
// 輸出 : "Here's a random number: 0.37464991998171"
println("And another one: \(generator.random())")
// 輸出 : "And another one: 0.729023776863283"
有時不得不在方法中更改實例的所屬類型。在基於值類型(value types)
(結構體,枚舉)的實例方法中,將mutating
關鍵字作為函數的前綴,寫在func
之前,表示可以在該方法中修改實例及其屬性的所屬類型。這一過程在Modifyting Value Types from Within Instance Methods章節中有詳細描述。
如果協議中的實例方法打算改變其遵循者
實例的類型,那麼在協議定義時需要在方法前加mutating
關鍵字,才能使結構體,枚舉
來採用並滿足協議中對方法的規定。
注意: 用
類
實現協議中的mutating
方法時,不用寫mutating
關鍵字;用結構體
,枚舉
實現協議中的mutating
方法時,必須寫mutating
關鍵字。
如下所示,Togglable
協議含有名為toggle
的突變實例方法。根據名稱推測,toggle
方法應該是用於切換或恢復其遵循者
實例或其屬性的類型。
protocol Togglable {
mutating func toggle()
}
當使用枚舉
或結構體
來實現Togglabl
協議時,需要提供一個帶有mutating
前綴的toggle
方法。
如下所示,OnOffSwitch
枚舉遵循
了Togglable
協議,On
,Off
兩個成員用於表示當前狀態。枚舉的toggle
方法被標記為mutating
,用以匹配Togglabel
協議的規定。
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 現在的值為 .On
儘管協議
本身並不實現任何功能,但是協議
可以被當做類型來使用。
使用場景:
協議類型
作為函數、方法或構造器中的參數類型或返回值類型協議類型
作為常量、變量或屬性的類型協議類型
作為數組、字典或其他容器中的元素類型
注意: 協議是一種類型,因此協議類型的名稱應與其他類型(Int,Double,String)的寫法相同,使用駝峰式寫法
如下所示,這個示例中將協議當做類型來使用
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
}
}
例子中又一個Dice
類,用來代表桌游中的擁有N個面的骰子。Dice
的實例含有sides
和generator
兩個屬性,前者是整型,用來表示骰子有幾個面,後者為骰子提供一個隨機數生成器。
generator
屬性的類型為RandomNumberGenerator
,因此任何遵循了RandomNumberGenerator
協議的類型的實例都可以賦值給generator
,除此之外,無其他要求。
Dice
類中也有一個構造器(initializer)
,用來進行初始化操作。構造器中含有一個名為generator
,類型為RandomNumberGenerator
的形參。在調用構造方法時創建Dice
的實例時,可以傳入任何遵循RandomNumberGenerator
協議的實例給generator。
Dice
類也提供了一個名為roll
的實例方法用來模擬骰子的面值。它先使用generator
的random
方法來創建一個[0-1]區間內的隨機數種子,然後加工這個隨機數種子生成骰子的面值。generator被認為是遵循了RandomNumberGenerator
的類型,因而保證了random
方法可以被調用。
如下所示,這裡展示了如何使用LinearCongruentialGenerator
的實例作為隨機數生成器創建一個六面骰子:
var d6 = Dice(sides: 6,generator: LinearCongruentialGenerator())
for _ in 1...5 {
println("Random dice roll is \(d6.roll())")
}
//輸出結果
//Random dice roll is 3
//Random dice roll is 5
//Random dice roll is 4
//Random dice roll is 5
//Random dice roll is 4
委託是一種設計模式(譯者注: 想起了那年 UITableViewDelegate 中的奔跑,那是我逝去的Objective-C。。。),它允許類
或結構體
將一些需要它們負責的功能交由(委託)
給其他的類型的實例。
委託模式的實現很簡單: 定義協議
來封裝
那些需要被委託的函數和方法
, 使其遵循者
擁有這些被委託的函數和方法
。
委託模式可以用來響應特定的動作或接收外部數據源提供的數據,而無需要知道外部數據源的所屬類型(譯者注:只要求外部數據源遵循
某協議)。
下文是兩個基於骰子遊戲的協議:
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
協議可以在任意含有骰子的遊戲中實現,DiceGameDelegate
協議可以用來追蹤DiceGame
的遊戲過程
如下所示,SnakesAndLadders
是Snakes and Ladders
(譯者注:Control Flow章節有該遊戲的詳細介紹)遊戲的新版本。新版本使用Dice
作為骰子,並且實現了DiceGame
和DiceGameDelegate
協議,後者用來記錄遊戲的過程:
class SnakesAndLadders: DiceGame {
let finalSquare = 25
let dic = Dice(sides: 6, generator: LinearCongruentialGenerator())
var square = 0
var board: Int[]
init() {
board = Int[](count: finalSquare + 1, repeatedValue: 0)
board[03] = +08; board[06] = +11; borad[09] = +09; board[10] = +02
borad[14] = -10; board[19] = -11; borad[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)
}
}
這個版本的遊戲封裝到了SnakesAndLadders
類中,該類採用了DiceGame
協議,並且提供了dice
屬性和play
實例方法用來遵循
協議。(dice
屬性在構造之後就不在改變,且協議只要求dice
為只讀的,因此將dice
聲明為常量屬性。)
在SnakesAndLadders
類的構造器(initializer)
初始化遊戲。所有的遊戲邏輯被轉移到了play
方法中,play
方法使用協議規定的dice
屬性提供骰子搖出的值。
注意:
delegate
並不是遊戲的必備條件,因此delegate
被定義為遵循DiceGameDelegate
協議的可選屬性,delegate
使用nil
作為初始值。
DicegameDelegate
協議提供了三個方法用來追蹤遊戲過程。被放置於遊戲的邏輯中,即play()
方法內。分別在遊戲開始時,新一輪開始時,遊戲結束時被調用。
因為delegate
是一個遵循DiceGameDelegate
的可選屬性,因此在play()
方法中使用了可選鏈
來調用委託方法。 若delegate
屬性為nil
, 則delegate所調用的方法失效。若delegate
不為nil
,則方法能夠被調用
如下所示,DiceGameTracker
遵循了DiceGameDelegate
協議
class DiceGameTracker: DiceGameDelegate {
var numberOfTurns = 0
func gameDidStart(game: DiceGame) {
numberOfTurns = 0
if game is SnakesAndLadders {
println("Started a new game of Snakes and Ladders")
}
println("The game is using a \(game.dice.sides)-sided dice")
}
func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) {
++numberOfTurns
println("Rolled a \(diceRoll)")
}
func gameDidEnd(game: DiceGame) {
println("The game lasted for \(numberOfTurns) turns")
}
}
DiceGameTracker
實現了DiceGameDelegate
協議規定的三個方法,用來記錄遊戲已經進行的輪數。 當遊戲開始時,numberOfTurns
屬性被賦值為0; 在每新一輪中遞加; 遊戲結束後,輸出打印遊戲的總輪數。
gameDidStart
方法從game
參數獲取遊戲信息並輸出。game
在方法中被當做DiceGame
類型而不是SnakeAndLadders
類型,所以方法中只能訪問DiceGame
協議中的成員。
DiceGameTracker
的運行情況,如下所示:
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
即便無法修改源代碼,依然可以通過擴展(Extension)
來擴充已存在類型(譯者注: 類,結構體,枚舉等)。擴展
可以為已存在的類型添加屬性
,方法
,下標
,協議
等成員。詳情請在擴展章節中查看。
注意: 通過
擴展
為已存在的類型遵循
協議時,該類型的所有實例也會隨之添加協議中的方法
TextRepresentable
協議含有一個asText
,如下所示:
protocol TextRepresentable {
func asText() -> String
}
通過擴展
為上一節中提到的Dice
類遵循TextRepresentable
協議
extension Dice: TextRepresentable {
func asText() -> String {
return "A \(sides)-sided dice"
}
}
從現在起,Dice
類型的實例可被當作TextRepresentable
類型:
let d12 = Dice(sides: 12,generator: LinearCongruentialGenerator())
println(d12.asText())
// 輸出 "A 12-sided dice"
SnakesAndLadders
類也可以通過擴展
的方式來遵循協議:
extension SnakeAndLadders: TextRepresentable {
func asText() -> String {
return "A game of Snakes and Ladders with \(finalSquare) squares"
}
}
println(game.asText())
// 輸出 "A game of Snakes and Ladders with 25 squares"
當一個類型已經實現了協議中的所有要求,卻沒有聲明時,可以通過擴展
來補充協議聲明:
struct Hamster {
var name: String
func asText() -> String {
return "A hamster named \(name)"
}
}
extension Hamster: TextRepresentable {}
從現在起,Hamster
的實例可以作為TextRepresentable
類型使用
let simonTheHamster = Hamster(name: "Simon")
let somethingTextRepresentable: TextRepresentable = simonTheHamester
println(somethingTextRepresentable.asText())
// 輸出 "A hamster named Simon"
注意: 即時滿足了協議的所有要求,類型也不會自動轉變,因此你必須為它做出明顯的協議聲明
協議類型可以被集合使用,表示集合中的元素均為協議類型:
let things: TextRepresentable[] = [game,d12,simoTheHamster]
如下所示,things
數組可以被直接遍歷,並調用其中元素的asText()
函數:
for thing in things {
println(thing.asText())
}
// A game of Snakes and Ladders with 25 squares
// A 12-sided dice
// A hamster named Simon
thing
被當做是TextRepresentable
類型而不是Dice
,DiceGame
,Hamster
等類型。因此能且僅能調用asText
方法
協議能夠繼承一到多個其他協議。語法與類的繼承相似,多個協議間用逗號,
分隔
protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
// 協議定義
}
如下所示,PrettyTextRepresentable
協議繼承了TextRepresentable
協議
protocol PrettyTextRepresentable: TextRepresentable {
func asPrettyText() -> String
}
遵循PrettyTextRepresentable
協議的同時,也需要遵循TextRepresentable
協議。
如下所示,用擴展
為SnakesAndLadders
遵循PrettyTextRepresentable
協議:
extension SnakesAndLadders: PrettyTextRepresentable {
func asPrettyText() -> String {
var output = asText() + ":\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
}
}
在for in
中迭代出了board
數組中的每一個元素:
- 當從數組中迭代出的元素的值大於0時,用
▲
表示 - 當從數組中迭代出的元素的值小於0時,用
▼
表示 - 當從數組中迭代出的元素的值等於0時,用
○
表示
任意SankesAndLadders
的實例都可以使用asPrettyText()
方法。
println(game.asPrettyText())
// A game of Snakes and Ladders with 25 squares:
// ○ ○ ▲ ○ ○ ▲ ○ ○ ▲ ▲ ○ ○ ○ ▼ ○ ○ ○ ○ ▼ ○ ○ ▼ ○ ▼ ○
一個協議可由多個協議採用protocol<SomeProtocol, AnotherProtocol>
這樣的格式進行組合,稱為協議合成(protocol composition)
。
舉個例子:
protocol Named {
var name: String { get }
}
protocol Aged {
var age: Int { get }
}
struct Person: Named, Aged {
var name: String
var age: Int
}
func wishHappyBirthday(celebrator: protocol<Named, Aged>) {
println("Happy birthday \(celebrator.name) - you're \(celebrator.age)!")
}
let birthdayPerson = Person(name: "Malcolm", age: 21)
wishHappyBirthday(birthdayPerson)
// 輸出 "Happy birthday Malcolm - you're 21!
Named
協議包含String
類型的name
屬性;Aged
協議包含Int
類型的age
屬性。Person
結構體遵循
了這兩個協議。
wishHappyBirthday
函數的形參celebrator
的類型為protocol<Named,Aged>
。可以傳入任意遵循
這兩個協議的類型的實例
注意:
協議合成
並不會生成一個新協議類型,而是將多個協議合成為一個臨時的協議,超出範圍後立即失效。
使用is
和as
操作符來檢查協議的一致性或轉化協議類型。檢查和轉化的語法和之前相同(詳情查看Typy Casting章節):
is
操作符用來檢查實例是否遵循
了某個協議
。as?
返回一個可選值,當實例遵循
協議時,返回該協議類型;否則返回nil
as
用以強制向下轉型。
@objc protocol HasArea {
var area: Double { get }
}
注意:
@objc
用來表示協議是可選的,也可以用來表示暴露給Objective-C
的代碼,此外,@objc
型協議只對類
有效,因此只能在類
中檢查協議的一致性。詳情查看*Using Siwft with Cocoa and Objectivei-c*。
如下所示,定義了Circle
和Country
類,它們都遵循了haxArea
協議
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 }
}
Circle
類把area
實現為基於存儲型屬性
radius的計算型屬性
,Country
類則把area
實現為存儲型屬性
。這兩個類都遵循
了haxArea
協議。
如下所示,Animal是一個沒有實現HasArea
協議的類
class Animal {
var legs: Int
init(legs: Int) { self.legs = legs }
}
Circle,Country,Animal
並沒有一個相同的基類,因而採用AnyObject
類型的數組來裝載在他們的實例,如下所示:
let objects: AnyObject[] = [
Circle(radius: 2.0),
Country(area: 243_610),
Animal(legs: 4)
]
objects
數組使用字面量初始化,數組包含一個radius
為2。0的Circle
的實例,一個保存了英國面積的Country
實例和一個legs
為4的Animal
實例。
如下所示,objects
數組可以被迭代,對迭代出的每一個元素進行檢查,看它是否遵循了
HasArea`協議:
for object in objects {
if let objectWithArea = object as? HasArea {
println("Area is \(objectWithArea.area)")
} else {
println("Something that doesn't have an area")
}
}
// Area is 12.5663708
// Area is 243610.0
// Something that doesn't have an area
當迭代出的元素遵循HasArea
協議時,通過as?
操作符將其可選綁定(optional binding)
到objectWithArea
常量上。objectWithArea
是HasArea
協議類型的實例,因此area
屬性是可以被訪問和打印的。
objects
數組中元素的類型並不會因為向下轉型
而改變,它們仍然是Circle
,Country
,Animal
類型。然而,當它們被賦值給objectWithArea
常量時,則只被視為HasArea
類型,因此只有area
屬性能夠被訪問。
可選協議含有可選成員,其遵循者
可以選擇是否實現這些成員。在協議中使用@optional
關鍵字作為前綴來定義可選成員。
可選協議在調用時使用可選鏈
,詳細內容在Optional Chaning章節中查看。
像someOptionalMethod?(someArgument)
這樣,你可以在可選方法名稱後加上?
來檢查該方法是否被實現。可選方法
和可選屬性
都會返回一個可選值(optional value)
,當其不可訪問時,?
之後語句不會執行,並整體返回nil
注意: 可選協議只能在含有
@objc
前綴的協議中生效。且@objc
的協議只能被類
遵循
如下所示,Counter
類使用含有兩個可選成員的CounterDataSource
協議類型的外部數據源來提供增量值(increment amount)
@objc protocol CounterDataSource {
@optional func incrementForCount(count: Int) -> Int
@optional var fixedIncrement: Int { get }
}
CounterDataSource
含有incrementForCount
的可選方法
和fiexdIncrement
的可選屬性
,它們使用了不同的方法來從數據源中獲取合適的增量值。
注意:
CounterDataSource
中的屬性和方法都是可選的,因此可以在類中聲明但不實現這些成員,儘管技術上允許這樣做,不過最好不要這樣寫。
Counter
類含有CounterDataSource?
類型的可選屬性dataSource
,如下所示:
@objc class Counter {
var count = 0
var dataSource: CounterDataSource?
func increment() {
if let amount = dataSource?.incrementForCount?(count) {
count += amount
} else if let amount = dataSource?.fixedIncrement? {
count += amount
}
}
}
count
屬性用於存儲當前的值,increment
方法用來為count
賦值。
increment
方法通過可選鏈
,嘗試從兩種可選成員
中獲取count
。
-
由於
dataSource
可能為nil
,因此在dataSource
後邊加上了?
標記來表明只在dataSource
非空時才去調用incrementForCount
方法。 -
即使
dataSource
存在,但是也無法保證其是否實現了incrementForCount
方法,因此在incrementForCount
方法後邊也加有?
標記
在調用incrementForCount
方法後,Int
型可選值
通過可選綁定(optional binding)
自動拆包並賦值給常量amount
。
當incrementForCount
不能被調用時,嘗試使用可選屬性fixedIncrement
來代替。
ThreeSource
實現了CounterDataSource
協議,如下所示:
class ThreeSource: CounterDataSource {
let fixedIncrement = 3
}
使用ThreeSource
作為數據源開實例化一個Counter
:
var counter = Counter()
counter.dataSource = ThreeSource()
for _ in 1...4 {
counter.increment()
println(counter.count)
}
// 3
// 6
// 9
// 12
TowardsZeroSource
實現了CounterDataSource
協議中的incrementForCount
方法,如下所示:
class TowardsZeroSource: CounterDataSource {
func incrementForCount(count: Int) -> Int {
if count == 0 {
return 0
} else if count < 0 {
return 1
} else {
return -1
}
}
}
下邊是執行的代碼:
counter.count = -4
counter.dataSource = TowardsZeroSource()
for _ in 1...5 {
counter.increment()
println(counter.count)
}
// -3
// -2
// -1
// 0
// 0