Swift學習之協議

協議

協議定義了用來實現某一特定任務或者功能的屬性、方法以及需要的東西。
類、結構體、枚舉都可以采用協議,但是都必須實現協議所必須的要求。除了采納協議規定的要求,還可以給協議擴展一下方法、屬性,這樣采納該協議的類型就能夠使用這些功能。

1.語法

protocol SomeProtocol1{
    //定義一下使用該協議要實現的內容
}
要讓自定義的類型采用協議,需要在需要的類型名后加上協議名,并用 “:” 隔開,如果采納多個協議,協議之間用逗號隔開。如果類型繼承某個類,需要將繼承的類寫在協議的最前面。
class someClass:SomeSuperClass,SomeProtocol{

}

2.屬性要求

協議可以要求采納該協議的類型提供特定名稱和類型的實例屬性或者類型屬性。但協議不指定協議的屬性是存儲還是計算型屬性。它指定名字和類型。此外,還可以指定屬性是可讀還是可寫的。
通常用var來指定屬性,在類型聲明后加{set get}來表明屬性是可讀還是可寫的。
protocol SomeProtocol2{
    var musetBeSettable:Int {set get} //如果有set,必須有get
    var doesnotBeSettable:Int {get}
}
在協議中定義類屬性的時候,使用static 關鍵字修飾,當類類型采納協議時,還可以用class 來聲明類型屬性
protocol FullName{
    var fullName:String{get}
}

struct Person:FullName {
    var fullName:String{ //提供FullName的屬性的get實現
        return "iyaqi"
    }
}
print(Person().fullName) // iyaqi

3.方法要求

協議可以要求采納該協議的類型必須實現指定的實例方法或者類方法,但是這些方法不需要提供具體的實現。類似屬性,可以類方法需用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。實現協議的 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 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.構造器要求

協議可以要求采納協議的類型實現指定的構造器。類似與普通的構造器一樣,但是不需要具體實現。
protocol aProtocol{
    init (someParameters:Int)
}
構造器在類中的實現
在類中的實現,無需指定是指定構造器還是便利構造器,并且需要加上 required 修飾符
class someClass:aProtocol{
    init(a:Int){}
    required init(someParameters: Int) {
        //這里是實現部分。
    }
}

class superClass {
    init(someParameters:Int){}
}

class subClass: superClass,aProtocol {
    //父類和協議都定義了這個構造器,所以要加上 override & required
    override required init( someParameters: Int) {
        
        super.init(someParameters: 1)
    }
}

5.協議作為類型

//協議可以 像普通類型一樣,作為參數、返回值,或者是屬性,數組、字典中等容器的元素。
class Dice{
    let sides:Int
    let generator :LinearCongruentialGenerator //協議作為屬性
    init(sides:Int,generator:LinearCongruentialGenerator){ //協議作為參數
        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.代理(委托)模式

委托是一種設計模式,它允許類和結構體將一些功能委托給其他類型的實例來操作。委托的實現很簡單:定義協議封裝那些要實現的功能,這樣采納該協議的類型就能夠提供這些功能。
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.通過擴展添加協議一致性

即便無法修改源代碼,依然可以用擴展令已有類型采納并符合協議。協議可以為類型添加屬性、方法、下標等來讓類型符合協議的要求
protocol TextRepresentable{
    var textualDescription:String{get}
}
extension Dice:TextRepresentable{
    var textualDescription:String{
        return "a side of Dice"
    }
}
通過擴展來采納并實現協議,跟在原始定義中實現是同樣的效果。

8.協議的集合

let things:[TextRepresentable] = [dice]
for thing in things{
    print(thing.textualDescription)
}

9.協議的繼承

協議能夠繼承一個或者多個協議,多個協議之間用逗號隔開,語法跟類的繼承相同。如果繼承的協議還繼承了其他協議,那么采納該協議的類型也必須實現其他協議的要求。比如,協議B 繼承 協議A,類型C采納了B協議,那么C必須也實現協議A規定的東西。

10.類類型專屬協議

可以在協議的繼承列表中,添加class 關鍵字表示該協議只能被類類型采納。其他類型不能采納。
protocol someProtocol4:class,TextRepresentable{
    //這里是類類型的定義部分
}

11.協議合成

如果采用多個協議,可以將多個協議放在protocol<>里面。表示采納的協議列表,稱協議合成,協議之間用逗號隔開
protocol Named{
    var name:String{get}
}
protocol Aged{
    var age:Int {get set}
}
struct People :Named,Aged{
    var name:String
    var age:Int
}   
協議合成,這個方法的參數不關心類型,之關心這個參數是否符合Named,Aged協議。
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.檢查協議一致性

使用is as來檢查協議一致性。即是否符合某協議,并且可以轉換到指定的協議類型
is 用來檢查實例是否符合某個協議,若符合則返回 true,否則返回 false。
as? 返回一個可選值,當實例符合某個協議時,返回類型為協議類型的可選值,否則返回 nil。
as! 將實例強制向下轉換到某個協議類型,如果強轉失敗,會引發運行時錯誤。

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.可選的協議要求

可以在協議中定義可以選擇實現的要求,采納該協議的類型可以選擇是否采用這些要求。用optional關鍵字修飾。注意,可選的協議要求只能用在標記@objc的協議中,標記@objc的協議只能被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.協議擴展

協議可以通過擴展來添加方法、屬性、下標等,基于此,可以通過給協議添加擴展,來實現這些功能,而無需在每個采納該協議的類型中添加這些功能。通過協議擴展,所有采納該協議的類型都會自動獲取到該擴展增加的方法實現,無需任何額外修改。
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
可以提供默認的實現,也可以為協議添加限制條件
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容