// swift的協議很重要
// 協議 protocol
//“協議 定義了一個藍圖,規定了用來實現某一特定任務或者功能的方法、屬性,以及其他需要的東西。類、結構體或枚舉都可以遵循協議,并為協議定義的這些要求提供具體實現。某個類型能夠滿足某個協議的要求,就可以說該類型遵循這個協議”
//“除了遵循協議的類型必須實現的要求外,還可以對協議進行擴展,通過擴展來實現一部分要求或者實現一些附加功能,這樣遵循協議的類型就能夠使用這些功能。”
//1. 協議語法
protocol SomeProtocol{
//這里是協議的定義部分
}
//“要讓自定義類型遵循某個協議,在定義類型時,需要在類型名稱后加上協議名稱,中間以冒號(:)分隔。遵循多個協議時,各協議之間用逗號(,)分隔:”
struct SomeStructure:SomeProtocol{
//結構體的定義部分
}
// “擁有父類的類在遵循協議時,應該將父類名放在協議名之前,以逗號分隔”
//class SomeClass: SomeSuperClass,FirstProtocol,AnotherProtocol{
//這里是類的定義部分
//}
//2. 屬性要求
//“協議可以要求遵循協議的類型提供特定名稱和類型的實例屬性或類型屬性。協議不指定屬性是存儲型屬性還是計算型屬性,它只指定屬性的名稱和類型。此外,協議還指定屬性是可讀的還是可讀可寫的。”
//“如果協議要求屬性是可讀可寫的,那么該屬性不能是常量屬性或只讀的計算型屬性。如果協議只要求屬性是可讀的,那么該屬性不僅可以是可讀的,如果代碼需要的話,還可以是可寫的。”
//“協議總是用 var 關鍵字來聲明變量屬性,在類型聲明后加上 { set get } 來表示屬性是可讀可寫的,可讀屬性則用 { get } 來表示:”
protocol propertyProtocol{
var mustBesettable:Int {
get set
}
var doesNotNeedToBeSettable:Int{
get
}
}
//“在協議中定義類型屬性時,總是使用 static 關鍵字作為前綴。當類類型遵循協議時,除了 static 關鍵字,還可以使用 class 關鍵字來聲明類型屬性:”
protocol AnotherProtocol{
static var someTypeProperty:Int{
get set
}
}
//示例
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 ncc1701 = Starship(name:"EnterPrise",prefix:"USS")
ncc1701.fullName //USSEnterPrise
//3.方法要求
//“協議可以要求遵循協議的類型實現某些指定的實例方法或類方法。這些方法作為協議的一部分,像普通方法一樣放在協議的定義中,但是不需要大括號和方法體。可以在協議中定義具有可變參數的方法,和普通方法的定義方式相同。但是,不支持為協議中的方法的參數提供默認值”
//“正如屬性要求中所述,在協議中定義類方法的時候,總是使用 static 關鍵字作為前綴。當類類型遵循協議時,除了 static 關鍵字,還可以使用 class 關鍵字作為前綴:”
protocol MethodProtocol{
static func someTypeMethod()
}
//示例
protocol RandomNumberGennerator{
func random() -> Double
}
class LinearCongruentialGenerator:RandomNumberGennerator{
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
}
}
//4. Mutating方法要求
// “有時需要在方法中改變方法所屬的實例。例如,在值類型(即結構體和枚舉)的實例方法中,將 mutating 關鍵字作為方法的前綴,寫在 func 關鍵字之前,表示可以在該方法中修改它所屬的實例以及實例的任意屬性的值。”
//“如果你在協議中定義了一個實例方法,該方法會改變遵循該協議的類型的實例,那么在定義協議時需要在方法前加 mutating 關鍵字。這使得結構體和枚舉能夠遵循此協議并滿足此方法要求。”
//“注意 實現協議中的 mutating 方法時,若是類類型,則不用寫 mutating 關鍵字。而對于結構體和枚舉,則必須寫 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 ligthSwitch = OnOffSwitch.Off
ligthSwitch.toggle()
//4. 構造器要求
//“協議可以要求遵循協議的類型實現指定的構造器。你可以像編寫普通構造器那樣,在協議的定義里寫下構造器的聲明,但不需要寫花括號和構造器的實體:”
protocol InitProtocol{
init(someParameter:Int)
}
//“你可以在遵循協議的類中實現構造器,無論是作為指定構造器,還是作為便利構造器。無論哪種情況,你都必須為構造器實現標上 required 修飾符”
class InitClass:InitProtocol{
required init(someParameter: Int) {
//構造器的實現
}
}
//“使用 required 修飾符可以確保所有子類也必須提供此構造器實現,從而也能符合協議。”
//“注意 如果類已經被標記為 final,那么不需要在協議構造器的實現中使用 required 修飾符,因為 final 類不能有子類。”
//“如果一個子類重寫了父類的指定構造器,并且該構造器滿足了某個協議的要求,那么該構造器的實現需要同時標注 required 和 override 修飾符:
protocol DoubleProtocol{
init()
}
class SuperClass{
init() {
}
}
class subClass: SuperClass,DoubleProtocol {
//因為遵循協議,需要加上required,
//因為繼承自父類,需要加上 override
required override init() {
//這里是構造器實現
}
}
//5.協議作為類型
//“盡管協議本身并未實現任何功能,但是協議可以被當做一個成熟的類型來使用。”
/*
“作為函數、方法或構造器中的 參數類型或返回值類型
作為常量、變量或屬性的類型
作為數組、字典或其他容器中的 元素類型”
*/
class Dice{
let sides : Int
let generator : RandomNumberGennerator
init(sides:Int,generator:RandomNumberGennerator) {
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 類還有一個構造器,用來設置初始狀態。構造器有一個名為 generator,類型為 RandomNumberGenerator 的形參。在調用構造方法創建 Dice 的實例時,可以傳入任何遵循 RandomNumberGenerator 協議的實例給 generator
var d6 = Dice(sides:6,generator:LinearCongruentialGenerator())
for _ in 1...5{
print("Random dice roll is \(d6.roll())")
}
//6.委托代理模式
// “委托是一種設計模式,它允許類或結構體將一些需要它們負責的功能委托給其他類型的實例,委托模式的實現很簡單:定義協議來封裝那些需要被委托的功能,這樣就能確保遵循協議的類型能提供這些功能。委托模式可以用來響應特定的動作,或者接收外部數據源提供的數據,而無需關心外部數據源的類型。
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 的游戲過程。”
class SnakesAndLadders:DiceGame{
let finalSquare = 25
let dice = Dice(sides:6,generator:LinearCongruentialGenerator())
var square = 0
var board : [Int]
init() {
board = [Int](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)
}
}
//“這個版本的游戲封裝到了 SnakesAndLadders 類中,該類遵循了 DiceGame 協議,并且提供了相應的可讀的 dice 屬性和 play() 方法。( dice 屬性在構造之后就不再改變,且協議只要求 dice 為可讀的,因此將 dice 聲明為常量屬性”
//“游戲使用 SnakesAndLadders 類的 init() 構造器來初始化游戲。所有的游戲邏輯被轉移到了協議中的 play() 方法,play() 方法使用協議要求的 dice 屬性提供骰子搖出的值。
//“注意,delegate 并不是游戲的必備條件,因此 delegate 被定義為 DiceGameDelegate 類型的可選屬性。因為 delegate 是可選值,因此會被自動賦予初始值 nil。隨后,可以在游戲中為 delegate 設置適當的值”
//“因為 delegate 是一個 DiceGameDelegate 類型的可選屬性,因此在 play() 方法中通過可選鏈式調用來調用它的方法。若 delegate 屬性為 nil,則調用方法會優雅地失敗,并不會產生錯誤。若 delegate 不為 nil,則方法能夠被調用,并傳遞 SnakesAndLadders 實例作為參數。”
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) sides 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")
}
}
//“DiceGameTracker 實現了 DiceGameDelegate 協議要求的三個方法,用來記錄游戲已經進行的輪數。當游戲開始時,numberOfTurns 屬性被賦值為 0,然后在每新一輪中遞增,游戲結束后,打印游戲的總輪數。”
//運行該游戲
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 sides dice
//rolled a 3
//rolled a 5
//rolled a 4
//rolled a 5
//the game lasted for 4 turns
//6.通過擴展添加協議一致性
//“即便無法修改源代碼,依然可以通過擴展令已有類型遵循并符合協議。擴展可以為已有類型添加屬性、方法、下標以及構造器,因此可以符合協議中的相應要求
//注意 通過擴展令已有類型遵循并符合協議時,該類型的所有實例也會隨之獲得協議中定義的各項功能。
protocol TextReprresentable{
var textualDescription : String {get}
}
extension Dice :TextReprresentable{
var textualDescription: String{
return "A \(sides) side dice"
}
}
//“通過擴展遵循并符合協議,和在原始定義中遵循并符合協議的效果完全相同。協議名稱寫在類型名之后,以冒號隔開,然后在擴展的大括號內實現協議要求的內容。”
let d12 = Dice(sides:12,generator:LinearCongruentialGenerator())
print(d12.textualDescription)
//打印 A 12 side dice
extension SnakesAndLadders:TextReprresentable{
var textualDescription: String{
return "A game of Snakes and ladders with \(finalSquare) squares"
}
}
print(game.textualDescription)
// 打印 A game of Snakes and ladders with 25 squares
//7. 通過擴展遵循協議
//“當一個類型已經符合了某個協議中的所有要求,卻還沒有聲明遵循該協議時,可以通過空擴展體的擴展來遵循該協議:”
struct Hamster{
var name : String
var textualDescription:String {
return "A hamster named \(name)"
}
}
extension Hamster:TextReprresentable{}
let simonTheHamster = Hamster(name:"simon")
print(simonTheHamster.textualDescription)
//打印A hamster named simon
//“注意 即使滿足了協議的所有要求,類型也不會自動遵循協議,必須顯式地遵循協議。
//8.協議類型的集合
//“協議類型可以在數組或者字典這樣的集合中使用”
let things:[TextReprresentable] = [game,d12,simonTheHamster]
for thing in things {
print(thing.textualDescription)
}
//打印 A game of Snakes and ladders with 25 squares
//A 12 side dice
//A hamster named simon
//“thing 是 TextRepresentable 類型而不是 Dice,DiceGame,Hamster 等類型,即使實例在幕后確實是這些類型中的一種。由于 thing 是 TextRepresentable 類型,任何 TextRepresentable 的實例都有一個 textualDescription 屬性,所以在每次循環中可以安全地訪問 thing.textualDescription。”
//9. 協議的繼承
//“協議能夠繼承一個或多個其他協議,可以在繼承的協議的基礎上增加新的要求。協議的繼承語法與類的繼承相似,多個被繼承的協議間用逗號分隔:”
protocol inheritingProtocol:SomeProtocol,AnotherProtocol{
//這里是協議的定義部分
}
protocol PrettyTextRepresentable:TextReprresentable{
var prettyTextualDescrition : String {get}
}
//“例子中定義了一個新的協議 PrettyTextRepresentable,它繼承自 TextRepresentable 協議。任何遵循 PrettyTextRepresentable 協議的類型在滿足該協議的要求時,也必須滿足 TextRepresentable 協議的要求。”
extension SnakesAndLadders:PrettyTextRepresentable{
var prettyTextualDescrition: 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
}
}
//“上述擴展令 SnakesAndLadders 遵循了 PrettyTextRepresentable 協議,并提供了協議要求的 prettyTextualDescription 屬性。每個 PrettyTextRepresentable 類型同時也是 TextRepresentable 類型,所以在 prettyTextualDescription 的實現中,可以訪問 textualDescription 屬性。然后,拼接上了冒號和換行符。”
print(game.prettyTextualDescrition)
//打印 〇〇↑〇〇↑〇〇↑↑〇〇〇↓〇〇〇〇↓〇〇↓〇↓〇
//10.類類型專屬協議
//“你可以在協議的繼承列表中,通過添加 class 關鍵字來限制協議只能被類類型遵循,而結構體或枚舉不能遵循該協議。class 關鍵字必須第一個出現在協議的繼承列表中,在其他繼承的協議之前”
protocol SomeClassOnlyProtocol:class,inheritingProtocol{
//類類型專屬協議
}
//注意:當協議定義的要求需要遵循協議的類型必須是引用語義而非值語義時,應該采用類類型專屬協議”
//11.協議合成
//“有時候需要同時遵循多個協議,你可以將多個協議采用 SomeProtocol & AnotherProtocol 這樣的格式進行組合,稱為 協議合成(protocol composition)。你可以羅列任意多個你想要遵循的協議,以與符號(&)分隔”
protocol Named{
var name : String{get}
}
protocol Aged {
var age : Int {get}
}
struct People:Named,Aged{
var name : String
var age : Int
init(_ name:String,_ age:Int) {
self.name = name
self.age = age
}
}
func wishHappyBirthday(to celebrator:Named&Aged){
print("happy birthday, \(celebrator.name),you are \(celebrator.age)")
}
let birthdayPerson = People("jack",12)
wishHappyBirthday(to: birthdayPerson)
//打印 happy birthday, jack,you are 12
//“Named 協議包含 String 類型的 name 屬性。Aged 協議包含 Int 類型的 age 屬性。Person 結構體遵循了這兩個協議。wishHappyBirthday(to:) 函數的參數 celebrator 的類型為 Named & Aged。這意味著它不關心參數的具體類型,只要參數符合這兩個協議即可。”
//“注意 協議合成并不會生成新的、永久的協議類型,而是將多個協議中的要求合成到一個只在局部作用域有效的臨時協議中。
//12.檢查協議的一致性
//“你可以使用類型轉換中描述的 is 和 as 操作符來檢查協議一致性,即是否符合某協議,并且可以轉換到指定的協議類型。檢查和轉換到某個協議類型在語法上和類型的檢查和轉換完全相同:”
//“is 用來檢查實例是否符合某個協議,若符合則返回 true,否則返回 false。”
//“as? 返回一個可選值,當實例符合某個協議時,返回類型為協議類型的可選值,否則返回 nil。”
//“as! 將實例強制向下轉換到某個協議類型,如果強轉失敗,會引發運行時錯誤。”
protocol HasArea{
var area:Double {get}
}
class Circle:HasArea{
let pi = 3.1415926
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
}
}
class Animal {
var legs : Int
init(legs:Int) {
self.legs = legs
}
}
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 habe an area")
}
}
//打印 Area is 12.5663704
//Area is 243610.0
//Something that doesn't habe an area
//“當迭代出的元素符合 HasArea 協議時,將 as? 操作符返回的可選值通過可選綁定,綁定到 objectWithArea 常量上。objectWithArea 是 HasArea 協議類型的實例,因此 area 屬性可以被訪問和打印。”
//“objects 數組中的元素的類型并不會因為強轉而丟失類型信息,它們仍然是 Circle,Country,Animal 類型。然而,當它們被賦值給 objectWithArea 常量時,只被視為 HasArea 類型,因此只有 area 屬性能夠被訪問。”
// 可選的協議要求
//“協議可以定義可選要求,遵循協議的類型可以選擇是否實現這些要求。在協議中使用 optional 關鍵字作為前綴來定義可選要求。可選要求用在你需要和 Objective-C 打交道的代碼中。協議和可選要求都必須帶上@objc屬性。標記 @objc 特性的協議只能被繼承自 Objective-C 類的類或者 @objc 類遵循,其他類以及結構體和枚舉均不能遵循這種協議”
//“使用可選要求時(例如,可選的方法或者屬性),它們的類型會自動變成可選的。比如,一個類型為 (Int) -> String 的方法會變成 ((Int) -> String)?。需要注意的是整個函數類型是可選的,而不是函數的返回值”
//“協議中的可選要求可通過可選鏈式調用來使用,因為遵循協議的類型可能沒有實現這些可選要求。類似 someOptionalMethod?(someArgument) 這樣,你可以在可選方法名稱后加上 ? 來調用可選方法”
@objc protocol CounterDataSource{
@objc optional func incrementForCount(count:Int)->Int
@objc optional var fixedIncrement:Int {get}
}
class Counter {
var count = 0
var dataSourece : CounterDataSource?
func increment(){
if let amount = dataSourece?.incrementForCount?(count: count) {
count += amount
}else if let amount = dataSourece?.fixedIncrement{
count += amount
}
}
}
//“這里使用了兩層可選鏈式調用。首先,由于 dataSource 可能為 nil,因此在 dataSource 后邊加上了 ?,以此表明只在 dataSource 非空時才去調用 increment(forCount:) 方法。其次,即使 dataSource 存在,也無法保證其是否實現了 increment(forCount:) 方法,因為這個方法是可選的。因此,increment(forCount:) 方法同樣使用可選鏈式調用進行調用,只有在該方法被實現的情況下才能調用它,所以在 increment(forCount:) 方法后邊也加上了 ?。”
class ThreeSource: NSObject,CounterDataSource{
let fixedIncrement = 3
}
var counter = Counter()
counter.dataSourece=ThreeSource()
for _ in 1...4{
counter.increment()
print(counter.count)
}
//打印 3
//6
//9
//12
@objc class TowardsZeroSource:NSObject,CounterDataSource{
func incrementForCount(count: Int) -> Int {
if count == 0 {
return 0
}else if count < 0 {
return 1
}else {
return -1
}
}
}
counter.count = -4
counter.dataSourece = TowardsZeroSource()
for _ in 1...5{
counter.increment()
print(counter.count)
}
//打印-3
//-2
//-1
//0
//0
//13.協議擴展
//“協議可以通過擴展來為遵循協議的類型提供屬性、方法以及下標的實現。通過這種方式,你可以基于協議本身來實現這些功能,而無需在每個遵循協議的類型中都重復同樣的實現,也無需使用全局函數。”
extension RandomNumberGennerator {
func randomBool() -> Bool {
return random() > 0.5
}
}
//“通過協議擴展,所有遵循協議的類型,都能自動獲得這個擴展所增加的方法實現,無需任何額外修改:”
let generator = LinearCongruentialGenerator()
print("here si a random Number: \(generator.random())")
//打印 here si a random Number: 0.37464991998171
print("and here is a random boolean:\(generator.randomBool())")
//打印 and here is a random boolean:true
//13.1 提供默認實現
// “可以通過協議擴展來為協議要求的屬性、方法以及下標提供 默認的實現 。如果遵循協議的類型為這些要求提供了自己的實現,那么這些自定義實現將會替代擴展中的默認實現被使用。”
//“注意 通過協議擴展為協議要求提供的默認實現和可選的協議要求不同。雖然在這兩種情況下,遵循協議的類型都無需自己實現這些要求,但是通過擴展提供的默認實現可以直接調用,而無需使用可選鏈式調用。
extension PrettyTextRepresentable{
var prettyTextualDescrition : String{
return textualDescription
}
}
//13.2 為協議擴展添加限制條件
//“在擴展協議的時候,可以指定一些限制條件,只有遵循協議的類型滿足這些限制條件時,才能獲得協議擴展提供的默認實現。這些限制條件寫在協議名之后,使用 where 子句來描述,正如Where子句中所描述的”
//“例如,你可以擴展 CollectionType 協議,但是只適用于集合中的元素遵循了 TextRepresentable 協議的情況”
//extension Collection where Generator.Element : TextReprresentable{
// var textualDescription : String{
// let itemsAsText = self.map{$0.textualDescription}
// return "[" + itemsAsText.joinWithSeparator(",") + "]"
// }
//}
//Array
//let murrayTheHamster = Hamster(name:"Murray")
//let morgenTheHamster = Hamster(name:"Morgan")
//let hamsters = [murrayTheHamster,morgenTheHamster]
//print(hamsters.textualDescription)
//應該打印“[A hamster named Murray, A hamster named Morgan]” 但是這里一直走不通