Swift-----協議Protocol


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

1 協議中可以定義屬性

  (1)屬性不可以有默認值

  (2)必須設置是“get”還是“get set”,注意:get與set之間是沒有逗號的

  (3)即使屬性只有get,沒有set,也不可以將屬性設置為let,必須設置為var

2 協議中可以定義方法

  (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 定義一個結構體實現該協議

  (1)實現協議中的屬性

    (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 an 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() }
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM