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