協(xié)議
協(xié)議定義了用來實現(xiàn)某一特定任務或者功能的屬性胳岂、方法以及需要的東西。
類找御、結構體元镀、枚舉都可以采用協(xié)議,但是都必須實現(xiàn)協(xié)議所必須的要求霎桅。除了采納協(xié)議規(guī)定的要求栖疑,還可以給協(xié)議擴展一下方法、屬性滔驶,這樣采納該協(xié)議的類型就能夠使用這些功能遇革。
1.語法
protocol SomeProtocol1{
//定義一下使用該協(xié)議要實現(xiàn)的內容
}
要讓自定義的類型采用協(xié)議,需要在需要的類型名后加上協(xié)議名揭糕,并用 “:” 隔開萝快,如果采納多個協(xié)議,協(xié)議之間用逗號隔開插佛。如果類型繼承某個類,需要將繼承的類寫在協(xié)議的最前面量窘。
class someClass:SomeSuperClass,SomeProtocol{
}
2.屬性要求
協(xié)議可以要求采納該協(xié)議的類型提供特定名稱和類型的實例屬性或者類型屬性雇寇。但協(xié)議不指定協(xié)議的屬性是存儲還是計算型屬性。它指定名字和類型蚌铜。此外,還可以指定屬性是可讀還是可寫的。
通常用var來指定屬性递胧,在類型聲明后加{set get}來表明屬性是可讀還是可寫的募寨。
protocol SomeProtocol2{
var musetBeSettable:Int {set get} //如果有set,必須有get
var doesnotBeSettable:Int {get}
}
在協(xié)議中定義類屬性的時候审葬,使用static 關鍵字修飾深滚,當類類型采納協(xié)議時,還可以用class 來聲明類型屬性
protocol FullName{
var fullName:String{get}
}
struct Person:FullName {
var fullName:String{ //提供FullName的屬性的get實現(xiàn)
return "iyaqi"
}
}
print(Person().fullName) // iyaqi
3.方法要求
協(xié)議可以要求采納該協(xié)議的類型必須實現(xiàn)指定的實例方法或者類方法涣觉,但是這些方法不需要提供具體的實現(xiàn)痴荐。類似屬性,可以類方法需用static關鍵字修飾官册,類類型還可以使用class修飾生兆。
protocol RandomNumberGenerator{
func random()->Double
//static func someMethod()
}
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()
generator.random() //0.3746499199817101
generator.random() //0.729023776863283
如果需要修改實例或者實例的屬性的值,需要在方法的func前面加 mutating膝宁。實現(xiàn)協(xié)議的 mutating方法時鸦难,如果是類類型根吁,在實現(xiàn)的方法前面不需要加 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 switcher = OnOffSwitch.Off
print("switcher's status:\(switcher)") //switcher's statu:Off\n
switcher.toggle()
print("switcher's status:\(switcher)") //switcher's statu:On\n
4.構造器要求
協(xié)議可以要求采納協(xié)議的類型實現(xiàn)指定的構造器。類似與普通的構造器一樣辈末,但是不需要具體實現(xiàn)愚争。
protocol aProtocol{
init (someParameters:Int)
}
構造器在類中的實現(xiàn)
在類中的實現(xiàn),無需指定是指定構造器還是便利構造器,并且需要加上 required 修飾符
class someClass:aProtocol{
init(a:Int){}
required init(someParameters: Int) {
//這里是實現(xiàn)部分挤聘。
}
}
class superClass {
init(someParameters:Int){}
}
class subClass: superClass,aProtocol {
//父類和協(xié)議都定義了這個構造器轰枝,所以要加上 override & required
override required init( someParameters: Int) {
super.init(someParameters: 1)
}
}
5.協(xié)議作為類型
//協(xié)議可以 像普通類型一樣,作為參數(shù)组去、返回值鞍陨,或者是屬性,數(shù)組从隆、字典中等容器的元素诚撵。
class Dice{
let sides:Int
let generator :LinearCongruentialGenerator //協(xié)議作為屬性
init(sides:Int,generator:LinearCongruentialGenerator){ //協(xié)議作為參數(shù)
self.sides = sides
self.generator = generator
}
func roll()->Int{
return Int(generator.random() * Double(sides)) + 1
}
}
var dice = Dice(sides: 3, generator: LinearCongruentialGenerator())
for _ in 1...5{
print(dice.roll()) //2,3,2,3,2
}
6.代理(委托)模式
委托是一種設計模式,它允許類和結構體將一些功能委托給其他類型的實例來操作键闺。委托的實現(xiàn)很簡單:定義協(xié)議封裝那些要實現(xiàn)的功能寿烟,這樣采納該協(xié)議的類型就能夠提供這些功能。
protocol DiceGame{
var dice:Dice {get}
func play()
}
protocol DiceGameDelegate{
func diceDidStart(game:DiceGame)
func game(game:DiceGame,didStartNewTurnWithDiceRoll dicRoll:Int)
func gameEnd(game:DiceGame)
}
class SnakeAndLadders: DiceGame {
let finalSquare = 25
var dice = Dice(sides: 6, generator: LinearCongruentialGenerator())
var square = 0
var board = [Int]()
init(){
board = [Int](count: finalSquare, 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?.diceDidStart(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?.gameEnd(self)
}
}
7.通過擴展添加協(xié)議一致性
即便無法修改源代碼辛燥,依然可以用擴展令已有類型采納并符合協(xié)議筛武。協(xié)議可以為類型添加屬性、方法挎塌、下標等來讓類型符合協(xié)議的要求
protocol TextRepresentable{
var textualDescription:String{get}
}
extension Dice:TextRepresentable{
var textualDescription:String{
return "a side of Dice"
}
}
通過擴展來采納并實現(xiàn)協(xié)議徘六,跟在原始定義中實現(xiàn)是同樣的效果。
8.協(xié)議的集合
let things:[TextRepresentable] = [dice]
for thing in things{
print(thing.textualDescription)
}
9.協(xié)議的繼承
協(xié)議能夠繼承一個或者多個協(xié)議榴都,多個協(xié)議之間用逗號隔開待锈,語法跟類的繼承相同。如果繼承的協(xié)議還繼承了其他協(xié)議嘴高,那么采納該協(xié)議的類型也必須實現(xiàn)其他協(xié)議的要求竿音。比如,協(xié)議B 繼承 協(xié)議A拴驮,類型C采納了B協(xié)議谍失,那么C必須也實現(xiàn)協(xié)議A規(guī)定的東西。
10.類類型專屬協(xié)議
可以在協(xié)議的繼承列表中莹汤,添加class 關鍵字表示該協(xié)議只能被類類型采納快鱼。其他類型不能采納。
protocol someProtocol4:class,TextRepresentable{
//這里是類類型的定義部分
}
11.協(xié)議合成
如果采用多個協(xié)議,可以將多個協(xié)議放在protocol<>里面抹竹。表示采納的協(xié)議列表线罕,稱協(xié)議合成,協(xié)議之間用逗號隔開
protocol Named{
var name:String{get}
}
protocol Aged{
var age:Int {get set}
}
struct People :Named,Aged{
var name:String
var age:Int
}
協(xié)議合成,這個方法的參數(shù)不關心類型窃判,之關心這個參數(shù)是否符合Named,Aged協(xié)議钞楼。
func wishHappyBirthday(celebrator:protocol<Named,Aged>){
print("Happy birthday \(celebrator.name) for \(celebrator.age)")
}
let birthdayPerson = People(name: "iyaqi", age: 25)
print(wishHappyBirthday(birthdayPerson))
12.檢查協(xié)議一致性
使用is as來檢查協(xié)議一致性。即是否符合某協(xié)議袄琳,并且可以轉換到指定的協(xié)議類型
is 用來檢查實例是否符合某個協(xié)議询件,若符合則返回 true,否則返回 false唆樊。
as? 返回一個可選值宛琅,當實例符合某個協(xié)議時,返回類型為協(xié)議類型的可選值逗旁,否則返回 nil嘿辟。
as! 將實例強制向下轉換到某個協(xié)議類型,如果強轉失敗片效,會引發(fā)運行時錯誤红伦。
protocol HasArea{
var area:Double{get}
}
class Circle: HasArea {
let pi:Double = 3.1415
var radius:Double
init(radius:Double){
self.radius = radius;
}
var area:Double{
return radius * radius * pi
}
}
class Country: HasArea {
var area:Double
init(area:Double){self.area = area}
}
class Animal {
var legs:Int
init(legs:Int){self.legs = legs}
}
var objects:[AnyObject] = [Circle(radius: 3),Country(area: 2000),Animal(legs: 2)]
for object in objects{
if let hasAreaType = object as? HasArea{
//在這里object會被轉化為 HasArea類型,雖然他們仍然是circle淀衣、country昙读、animal類型,當object 賦給 hasAreaType時膨桥,只能訪問area屬性
print(hasAreaType.area) // 28.2735 ,200
}
}
13.可選的協(xié)議要求
可以在協(xié)議中定義可以選擇實現(xiàn)的要求蛮浑,采納該協(xié)議的類型可以選擇是否采用這些要求。用optional關鍵字修飾国撵。注意陵吸,可選的協(xié)議要求只能用在標記@objc的協(xié)議中玻墅,標記@objc的協(xié)議只能被OC的類或者是@objc類采納介牙,其他類型均不能采納
@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
}
}
}
var counter = Counter()
class threeSource: NSObject,CounterDataSource {
let fixedIncrement = 3
}
counter.datasource = threeSource()
for _ in 1..<4{
counter.increment() //
print(counter.count) // 3 6 9
}
class TowardsZeroSource:NSObject,CounterDataSource{
func incrementForCount(count: Int) -> Int {
if count == 0 {
return 0
}else if count < 0{
return 1
}else{
return -1
}
}
}
print(counter.count) //9
counter.datasource = TowardsZeroSource()
counter.increment()
print(counter.count) //8
14.協(xié)議擴展
協(xié)議可以通過擴展來添加方法、屬性澳厢、下標等环础,基于此,可以通過給協(xié)議添加擴展剩拢,來實現(xiàn)這些功能线得,而無需在每個采納該協(xié)議的類型中添加這些功能。通過協(xié)議擴展徐伐,所有采納該協(xié)議的類型都會自動獲取到該擴展增加的方法實現(xiàn)贯钩,無需任何額外修改。
extension RandomNumberGenerator{
func randomBool()->Bool{
return random() > 0.5
}
}
let generator2 = LinearCongruentialGenerator()
print(generator.random()) // 0.636466906721536
print(generator.randomBool()) // 0.636466906721536 > 0.5 返回 true
可以提供默認的實現(xiàn),也可以為協(xié)議添加限制條件