Swift -Protocol

// 本文內容來自慕課網----玩兒轉Swiftswift

1 協議中能夠定義屬性app

  (1)屬性不能夠有默認值iphone

  (2)必須設置是「get」仍是「get set」,注意:get與set之間是沒有逗號的函數

  (3)即便屬性只有get,沒有set,也不能夠將屬性設置爲let,必須設置爲varui

2 協議中能夠定義方法spa

  (1)方法的參數不能夠有默認值設計

複製代碼
protocol Pet {
    
    // 定義屬性
    var name: String {get set}
    var birthplace: String {get}    

    // 定義方法
    func fed(food: String)
    func playWith()
   mutating func changeName(name: String) }
 
複製代碼
3 如下寫法中,表示pet遵照Pet協議。因爲Pet不是類,故不能用Pet()來對pet進行初始化。
var pet: Pet 

4 定義一個結構體實現該協議code

  (1)實現協議中的屬性orm

    (I)此時屬性能夠設置默認值對象

    (II)協議中name爲可讀可寫的,能夠直接聲明爲var類型就能夠

    (III)協議中birthplace爲可讀屬性,能夠直接聲明爲let類型就能夠,固然也能夠聲明爲var類型

  (2)實現協議中的方法

    (I)能夠爲方法中的參數設置默認值

    (II)在結構體中,若是須要改變自身的值,須要在方法前面加mutating關鍵字。在協議的方法中添加mutating關鍵字,若是結構體來遵照協議,須要有  mutating這個關鍵字,若是是類來遵照協議,mutating關鍵字就不須要了。

複製代碼
struct Dog: Pet {

    var name: String = "Tim"
    let birthplace: String = "Bei Jing"
    
    func fed(food: String = "Bone") {
        
        if food == "Bone" {
            print("I am happy.")
        } else {
            print("I need a bone.")
        }
    }
    
    func playWith() {
         print("Wong!")
    }
    
    mutating func changeName(name: String) {
        self.name = name
    }
}
複製代碼

 5 將一個Dog類賦值給一個遵照了Pet協議的對象,是沒有問題的。由於協議能夠看成一個類型來看待。

var dog: Dog = Dog()
let aPet: Pet = dog

6 若是隻但願協議只被類class遵照,只須要在定義協議的時候在後面加上AnyObject便可。

 如下定義的Animate協議只能被類遵照,結構體是不能夠遵照的。

複製代碼
protocol Animate: AnyObject {
    var name: String {get set}
}

class Cat: Animate {
    
    var name: String = ""
}
複製代碼

7 Dog類在實現協議Pet的時候,若是將birthplace聲明爲var是沒有問題的。若是birthplace被看成是Dog的屬性,它是能夠賦值的,但若是birthplace被做爲是Pet的屬性,它是不能夠賦值的。

複製代碼
struct Dog: Pet {
    // 其他的屬性和方法省略
    var birthplace: String = "Beijing"
}

var dog: Dog = Dog()
let aPet: Pet = dog

// 對dog的birthplace屬性賦值是沒有問題的 dog.birthplace = "Shanghai"
// 不能夠對aPet的birthplace屬性賦值。由於在協議Pet中,birthplace是隻讀的
// aPet.birthplace = "Hangzhou"
複製代碼

 8 若是協議(Protocol Pet)中定義了構造函數(init),則實現協議的類(Dog)必須實現這個構造函數,而繼承該類(Dog)的子類(Taidi)間接的也實現了該協議,故子類(Taidi)也必須實現這個構造函數。所以須要在類(Dog)的構造函數中填加子類(Taidi)必須實現該方法的標識,即關鍵字required。若是類(Dog)定義爲final類,即其它類不能夠繼承該類,則required關鍵字能夠省略。

複製代碼
protocol Pet {
    
    var name: String {get set}
    var birthplace: String {get}
    
    // 定義構造函數
    init(name: String)
}

class Animate {
    
    var type = "mamal"
}

class Dog: Animate, Pet {

    var name: String = "Tim"
    var birthplace: String = "Bei Jing"
    
    required init(name: String) {
        self.name = name
    }
}

// Dog類也能夠這樣寫
final class Dog: Animate, Pet {
    
    var name: String = "Tim"
    var birthplace: String = "Bei Jing"
    // 此時required關鍵字就能夠省略
    init(name: String) {
        self.name = name
    }
}
複製代碼

 9 類型別名(typealias)和關聯類型(associatedtype)

  類型別名(typealias)其實就是給類型從新定義一個名字。  

複製代碼
extension Double {    
    var km: Double {return self * 1000.0}
    var m: Double {return self}
    var cm: Double {return self / 100}
    var ft: Double {return self / 3.28084}
}
let runningDistance: Double = 3.54.km

// 咱們能夠給Double設置一個別名Length,則以上代碼就能夠改成下面的代碼
typealias Length = Double
extension Double {
    var km: Length {return self * 1000.0}
    var m: Length {return self}
    var cm: Length {return self / 100}
    var ft: Length {return self / 3.28084}
}
let runningDistance: Length = 3.54.km
複製代碼

  在設計協議(protocol)時,若是有兩個協議,它們的方法和屬性都同樣,只有協議中用到的類型不一樣,咱們就不必定義兩個協議,只須要將這兩個協議合併爲一個就能夠了。這時就能夠在協議中使用關聯類型(associatedtype),相似於類型別名(typealias)。

複製代碼
protocol WeightCalaulate {

    associatedtype weightType
    
    var weight: weightType {get}
}
// 在類iphone7中,weightType爲Double類型的別名
class iphone7: WeightCalaulate {

    typealias weightType = Double
    
    var weight: weightType {
        return 0.114
    }
}
// 在類Ship中,weightType爲Int類型的別名
class Ship: WeightCalaulate {

    typealias weightType = Int
    
    var weight: weightType {
        return 46_328_000
    }
}
複製代碼

10 協議能夠繼承、能夠擴展

  (1)先看下面的代碼:定義了一個協議Record,兩個結構體BaseballRecord、BasketballRecord,這兩個結構體都實現了Record和CustomStringConvertible協議。

複製代碼
protocol Record {
    var wins: Int {get}
    var loses: Int {get}
    func winningPercent() -> Double
}

struct BaseballRecord: Record, CustomStringConvertible {
    var wins: Int
    var loses: Int
    func winningPercent() -> Double {
        return Double(wins)/Double(wins + loses)
    }
    
    var description: String {
        return String(format: "WINS: %d, LOSES: %d", wins, loses)
    }
}

struct BasketballRecord: Record, CustomStringConvertible {  
    var wins: Int
    var loses: Int
    func winningPercent() -> Double {
        return Double(wins)/Double(wins + loses)
    }
var description: String { return String(format: "WINS: %d, LOSES: %d", wins, loses) } }
複製代碼

  (2)能夠看到,以上兩個結構體都實現了Record, CustomStringConvertible兩個協議,若是咱們但願只要實現Record協議,就須要實現CustomStringConvertible協議。咱們可讓Record協議繼承自CustomStringConvertible協議,這樣只要實現Record協議,就必須實現CustomStringConvertible協議。代碼以下:

複製代碼
// 協議中定義代碼及結構體中實現的代碼同上,此處省略
protocol Record: CustomStringConvertible { } struct BaseballRecord: Record { } struct BasketballRecord: Record { }
複製代碼

  (3)能夠看到,兩個結構體中實現協議的代碼是相同的。若是這一部分代碼能夠寫到協議中,在結構體中就能夠省去重複的代碼。又協議的定義(Protocol Record)中是不能夠實現代碼的,咱們能夠經過擴展協議的方式,在擴展中實現相應的代碼。(在擴展中能夠進行一些默認的實現)

複製代碼
protocol Record: CustomStringConvertible {
    var wins: Int {get}
    var loses: Int {get}
    func winningPercent() -> Double
}

extension Record {
  // 定義一個屬性   
    var gamePlayed: Int {
        return wins + loses
    }
    // 實現Record協議中定義的方法
    func winningPercent() -> Double {
        return Double(wins)/Double(gamePlayed)
    }
    // 實現CustomStringConvertible協議
    var description: String {
        return String(format: "WINS: %d, LOSES: %d", wins, loses)
    }
} struct BaseballRecord: Record { var wins: Int var loses: Int } struct BasketballRecord: Record { var wins: Int var loses: Int } let baseball = BaseballRecord(wins: 3, loses: 2) baseball.winningPercent() print(baseball) // WINS: 3, LOSES: 2
複製代碼

  (4)能夠擴展系統中的協議

複製代碼
extension CustomStringConvertible {
    var descriptionWithDate: String {
        return NSDate().description + description
    }
}

baseball.descriptionWithDate
複製代碼

  (5)在協議的擴展中定義的屬性,在實現該協議的結構體中仍能夠重寫該屬性。 

struct BaseballRecord: Record {
    var wins: Int
    var loses: Int
    // 重寫了協議擴展中定義的屬性gamePlayed
    let gamePlayed: Int = 162
}

  (6)用where關鍵字對協議作條件限定(where 類型限定)

    (I)第一一個結構體FootballRecord,它實現Record協議,而且它新增了一個屬性(平局ties),這樣它的gamePlayed屬性以及winningPercent()方法都須要重寫。代碼以下:

複製代碼
struct FootballRecord: Record {
    var wins: Int
    var loses: Int
    // 定義平局的屬性
    var ties: Int
    
    var gamePlayed: Int {
        return wins + loses + ties
    }
    
    func winningPercent() -> Double {
        return Double(wins)/Double(gamePlayed)
    }
}

let football = FootballRecord(wins: 1, loses: 1, ties: 1)
football.gamePlayed
football.winningPercent()
複製代碼

    (II)若是按上面這樣寫,那麼具備「平局屬性」的那些結構體都須要寫上面的那些所有代碼。故咱們能夠再寫一個協議Tieable,而實現了該協議Tieable的結構體的gamePlayed屬性及winningPercent()方法都應該從新定義。能夠對Record協議進行擴展,而且在擴展中增長限制,只有實現了Tieable協議的那些結構體才能夠有這個擴展中的方法和屬性。

複製代碼
// 既實現了Record協議,又實現了Tieable協議的結構體(類),纔可使用這個擴展中的屬性、方法
extension Record where Self: Tieable {
    
    var gamePlayed: Int {
        return wins + loses + ties
    }
    
    func winningPercent() -> Double {
        return Double(wins)/Double(gamePlayed)
    }
}
struct FootballRecord: Record, Tieable {

    var wins: Int
    var loses: Int

    var ties: Int
}

let football = FootballRecord(wins: 1, loses: 1, ties: 1)
football.gamePlayed      // 3
football.winningPercent()  // 0.3333333333
複製代碼

   (7)協議聚合 

複製代碼
protocol Prizable {    
    func isPrizable() -> Bool
}

// 表示Prizable和CustomStringConvertible兩個協議都實現了的結構體才能夠調用該方法
func award(one: Prizable & CustomStringConvertible) {
    
}
複製代碼

  (8)泛型約束(在方法定義中能夠用T來表明某個協議,只須要用<T: 協議名>來定義協議就好)

func top<T: Record>(seq: [T]) -> T {    
}
// 多個協議
func top<T: Record & Prizable>(seq: [T]) -> T {
}

  (9)可選協議:如下協議被標識爲@objc屬性,使得它兼容Objective-C代碼。若是協議擁有可選的屬性或方法時,是必須添加@objc的,由於Swift要使用Objective-C的運行時來檢查類所遵照的可選方法是否存在。擁有可選方法的協議只能被類遵照,結構體和枚舉是不能夠遵照該協議的。

@objc protocol Animal {  // 注意: 在swift3中optional前面也必須有@objc
    @objc optional func fly()
} 
相關文章
相關標籤/搜索