//协议(Protocols)
import UIKit
/*协议(Protocols)
1.协议定义了一个蓝图,规定了用来实现某一特定任务或者功能的方法、属性,以及其他需要的东西
2.类、结构体或枚举都可以采纳协议,并为协议定义的这些要求提供具体实现。某个类型能够满足某个协议的要求,就可以说该类型“符合”这个协议。
3.除了采纳协议的类型必须实现的要求外,还可以对协议进行扩展,通过扩展来实现一部分要求或者实现一些附加功能,这样采纳协议的类型就能够使用这些功能。
协议定义语法: protocol SomeProtocol { // 这里是协议的定义部分 }
协议的采纳:拥有父类的类在采纳协议时,应该将父类名放在协议名之前,以逗号分隔:
class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
// 这里是类的定义部分
}
属性要求:
1.协议可以要求采纳协议的类型提供特定名称和类型的实例属性或类型属性。协议不指定属性是存储型属性还是计算型属性,它只指定属性的名称和类型。
2.协议还指定属性是只读的还是可读可写的
3.如果协议要求属性是可读可写的,那么该属性不能是常量属性或只读的计算型属性。如果协议只要求属性是只读的,那么该属性不仅可以是只读的,如果代码需要的话,还可以是可写的
protocol SomeProtocol {
var mustBeSettable: Int { get set } //表示属性是可读可写的
static var doesNotNeedToBeSettable: Int { get } //类型属性、只读属性,static 关键字,类类型的还可以使用 class 关键字来声明类型属性
}
方法要求:
1.协议可以要求采纳协议的类型实现某些指定的实例方法或类型方法。这些方法作为协议的一部分,像普通方法一样放在协议的定义中,但是不需要大括号和方法体。可以在协议中定义具有可变参数的方法,和普通方法的定义方式相同。但是,不支持为协议中的方法的参数提供默认值。
2.在协议中定义类型方法的时候,总是使用 static 关键字作为前缀。当类类型采纳协议时,除了 static 关键字,还可以使用 class 关键字作为前缀
3.值类型的Mutating 方法要求:若需要在值类型的方法中修改方法所属的实例,将 mutating 关键字作为方法的前缀
构造器要求:协议可以要求采纳协议的类型实现指定的构造器
1.你可以在采纳协议的类中实现构造器,无论是作为指定构造器,还是作为便利构造器。无论哪种情况,你都必须为类的构造器实现标上 required 修饰符:
2.使用 required 修饰符可以确保所有子类也必须提供此构造器实现,从而也能符合协议
3.如果一个子类重写了父类的指定构造器,并且该构造器满足了某个协议的要求,那么该构造器的实现需要同时标注 required 和 override 修饰符
协议作为类型:
1.尽管协议本身并未实现任何功能,但是协议可以被当做一个成熟的类型来使用。
2.协议可以像其他普通类型一样使用,使用场景如下:
1.作为函数、方法或构造器中的参数类型或返回值类型
2.作为常量、变量或属性的类型
3.作为数组、字典或其他容器中的元素类型
委托(代理)模式:嵌套
1.委托是一种设计模式,它允许类或结构体将一些需要它们负责的功能委托给其他类型的实例。
2.委托模式的实现很简单:定义协议来封装那些需要被委托的功能,这样就能确保采纳协议的类型能提供这些功能。
3.委托模式可以用来响应特定的动作,或者接收外部数据源提供的数据,而无需关心外部数据源的类型
通过扩展采纳协议:
1.即便无法修改源代码,依然可以通过扩展令已有类型采纳并符合协议。扩展可以为已有类型添加属性、方法、下标脚本以及构造器,因此可以符合协议中的相应要求。通过扩展令已有类型采纳并符合协议时,该类型的所有实例也会随之获得协议中定义的各项功能。
2.当一个类型已经符合了某个协议中的所有要求,却还没有声明采纳该协议时,可以通过空扩展体的扩展来采纳该协议
协议类型的集合:协议类型可以在数组或者字典这样的集合中使用,所有采纳某协议的类型实例,虽然类型各不相同,但都是该协议类型
协议的继承:协议能够继承一个或多个其他协议,可以在继承的协议的基础上增加新的要求。协议的继承语法与类的继承相似,多个被继承的协议间用逗号分隔:
protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
// 这里是协议的定义部分
}
类类型专属协议:在协议的继承列表中,通过添加 class 关键字来限制协议只能被类类型采纳,如果尝试让结构体或枚举类型采纳该协议,则会导致编译错误。
protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
// 这里是类类型专属协议的定义部分
}
协议合成:有时候需要同时采纳多个协议,你可以将多个协议采用 protocol<SomeProtocol, AnotherProtocol> 这样的格式进行组合,称为 协议合成(protocol composition)。你可以在 <> 中罗列任意多个你想要采纳的协议,以逗号分隔
检查协议一致性:
1.你可以使用类型转换中描述的 is 和 as 操作符来检查协议一致性,即是否符合某协议,并且可以转换到指定的协议类型。
2.检查和转换到某个协议类型在语法上和类型的检查和转换完全相同:
is 用来检查实例是否符合某个协议,若符合则返回 true,否则返回 false。
as? 返回一个可选值,当实例符合某个协议时,返回类型为协议类型的可选值,否则返回 nil。
as! 将实例强制向下转换到某个协议类型,如果强转失败,会引发运行时错误。
可选的协议要求:
1.协议可以定义可选要求,采纳协议的类型可以选择是否实现这些要求。
2.在协议中使用 optional 关键字作为前缀来定义可选要求。
3.使用可选要求时(例如,可选的方法或者属性),它们的类型会自动变成可选的
4.协议中的可选要求可通过可选链式调用来使用,因为采纳协议的类型可能没有实现这些可选要求
5.可选的协议要求只能用在标记 @objc 特性的协议中。该特性表示协议将暴露给 Objective-C 代码,详情参见Using Swift with Cocoa and Objective-C(Swift 2.1)。即使你不打算和 Objective-C 有什么交互,如果你想要指定可选的协议要求,那么还是要为协议加上 @obj 特性。还需要注意的是,标记 @objc 特性的协议只能被继承自 Objective-C 类的类或者 @objc 类采纳,其他类以及结构体和枚举均不能采纳这种协议。
协议扩展:
1.协议可以通过扩展来为采纳协议的类型提供属性、方法以及下标脚本的实现。通过这种方式,你可以基于协议本身来实现这些功能,而无需在每个采纳协议的类型中都重复同样的实现,也无需使用全局函数
2.提供默认实现:可以通过协议扩展来为协议要求的属性、方法以及下标脚本提供默认的实现。如果采纳协议的类型为这些要求提供了自己的实现,那么这些自定义实现将会替代扩展中的默认实现被使用。
3.为协议扩展添加限制条件:在扩展协议的时候,可以指定一些限制条件,只有采纳协议的类型满足这些限制条件时,才能获得协议扩展提供的默认实现。这些限制条件写在协议名之后,使用 where 子句来描述
*/
protocol FullyNamed {
var fullName: String { get }
}
struct Person: FullyNamed {
var fullName: String
}
let john = Person(fullName: "John Appleseed") // john.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 是 "USS Enterprise"
//========================
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) % m)
return lastRandom / m
}
}
let generator = LinearCongruentialGenerator()
print("And another one: \(generator.random())") //每执行一次,lastRandom的值就被改变一次
print("And another one: \(generator.random())")
//=====================
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
}
}
var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator())
for _ in 1...5 {
print("Random dice roll is \(d6.roll())")
}
//=======================
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)
}
class SnakesAndLadders: DiceGame {
let finalSquare = 25
let dice = 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; 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)
}
}
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
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()
//===========================
protocol TextRepresentable {
var textualDescription: String { get }
}
extension Dice: TextRepresentable { //扩展Dice类来采纳并符合协议
var textualDescription: String {
return "A \(sides)-sided dice"
}
}
let d12 = Dice(sides: 12,generator: LinearCongruentialGenerator())
print(d12.textualDescription)
extension SnakesAndLadders: TextRepresentable {
var textualDescription: String {
return "A game of Snakes and Ladders with \(finalSquare) squares"
}
}
print(game.textualDescription)
//==============
struct Hamster {
var name: String
var textualDescription: String {
return "A hamster named \(name)"
}
}
extension Hamster: TextRepresentable {} //本身已经符合了协议的所有要求,可通过空扩展来采纳协议
let simonTheHamster = Hamster(name: "Simon")
let somethingTextRepresentable: TextRepresentable = simonTheHamster
print(somethingTextRepresentable.textualDescription)
//==================
let things: [TextRepresentable] = [game, d12, simonTheHamster]
for thing in things {
print(thing.textualDescription)
}
//========================
protocol PrettyTextRepresentable: TextRepresentable {
var prettyTextualDescription: String { get }
}
extension SnakesAndLadders: PrettyTextRepresentable {
//每个 PrettyTextRepresentable 类型同时也是 TextRepresentable 类型
//所以在 prettyTextualDescription 的实现中,可以访问 textualDescription 属性
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)
//=============================
protocol Named {
var name: String { get }
}
protocol Aged {
var age: Int { get }
}
struct Person1: Named, Aged {
var name: String
var age: Int
}
func wishHappyBirthday(celebrator: protocol<Named, Aged>) { //同时采纳多个协议
print("Happy birthday \(celebrator.name) - you're \(celebrator.age)!")
}
let birthdayPerson = Person1(name: "Malcolm", age: 21)
wishHappyBirthday(birthdayPerson)
//============================
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 }
}
class Animal {
var legs: Int
init(legs: Int) { self.legs = legs }
}
let objects: [AnyObject] = [ //它们都是类,它们的实例都可以作为 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")
}
}
//================================
@objc protocol CounterDataSource {
optional func incrementForCount(count: Int) -> Int
optional var fixedIncrement: Int { get }
}
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
}
}
}
class ThreeSource: NSObject, CounterDataSource {
let fixedIncrement = 3
}
var counter = Counter()
counter.dataSource = ThreeSource()
for _ in 1...4 {
counter.increment()
print(counter.count)
}
@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.dataSource = TowardsZeroSource()
for _ in 1...5 {
counter.increment()
print(counter.count)
}
//=============================
//通过协议扩展,所有采纳协议的类型,都能自动获得这个扩展所增加的方法实现,无需任何额外修改:
extension RandomNumberGenerator {
func randomBool() -> Bool {
return random() > 0.5
}
}
let generator1 = LinearCongruentialGenerator()
print("Here's a random number: \(generator1.random())")
// 打印 “Here's a random number: 0.37464991998171”
print("And here's a random Boolean: \(generator1.randomBool())")
// 打印 “And here's a random Boolean: true”
//=======
extension CollectionType where Generator.Element: TextRepresentable { //为协议的扩展添加限制条件
var textualDescription: String {
let itemsAsText = self.map { $0.textualDescription }
return "[" + itemsAsText.joinWithSeparator(", ") + "]"
}
}