協(xié)議的命名遵循 Swift 的標(biāo)準(zhǔn)庫, 即協(xié)議名以 "Type", "-able", "-ible" 結(jié)尾
例如 SequenceType, GeneratorType, CustomStringConvertible, -type 定義行為, -able 定義元素怎樣做某事。
protocol ExerciseType: CustomStringConvertible {
var name: String { get }
var caloriesBurned: Double { get }
var minutes: Double { get }
}
// 結(jié)構(gòu)體遵守 ErerciseType 協(xié)議, 但是也可以有自己的屬性和方法, 這也是一種繼承和封裝
struct EllipticalTrainer: ExerciseType {
let name = "Elliptical Machine"
let caloriesBurned: Double
let minutes: Double
}
struct Treadmill: ExerciseType {
let name = "Treadmill"
let caloriesBurned: Double
let minutes: Double
let distancesInMiles: Double
}
extension Treadmill {
var description: String {
return "Treadmill(\(caloriesBurned) calories and \(distancesInMiles) miles in \(minutes) minutes)"
}
}
let ellipticalWorkout = EllipticalTrainer(caloriesBurned: 335, minutes: 30)
let runningWorkout = Treadmill(caloriesBurned: 350, minutes: 25, distancesInMiles: 4.2)
擴(kuò)展 ExerciseType
有尖括號<>, 表明這是泛型函數(shù), 而占位符類型(placeholder type)要求遵守 ExerciseType 協(xié)議
// 函數(shù)體中用了兩個 ExerciseType 的屬性來計算每分鐘燃燒的卡路里
func caloriesPerMinutes<Exercise: ExerciseType>(exercise: Exercise) -> Double {
return exercise.caloriesBurned / exercise.minutes
}
print(caloriesPerMinutes(ellipticalWorkout))
print(caloriesPerMinutes(runningWorkout))
caloriesBurnedPerMinute(_:) 沒有一點(diǎn)錯誤, 但是每當(dāng)你擁有一個 ExerciseType 的實例, 你必須記住 caloriesBurnedPerMinute(_:) 函數(shù)的存在。所以讓每一個 ExerciseType 都有一個 caloriesBurnedPerMinute 屬性更適合 —— 但是你不想把同樣一份實現(xiàn)拷貝進(jìn) EllipticalTrainer 和 Treadmill 結(jié)構(gòu)體中(或者你新創(chuàng)建的 ExerciseType 中)
Swift 能擴(kuò)展協(xié)議
擴(kuò)展 ExerciseType 協(xié)議來添加一個屬性
協(xié)議擴(kuò)展能添加已經(jīng)實現(xiàn)的屬性和方法, 但是不能為協(xié)議添加新的必須要實現(xiàn)的屬性和方法
很像你寫泛型函數(shù)那樣, 協(xié)議擴(kuò)展的內(nèi)部實現(xiàn)只能訪問保證存在的其它屬性的方法
添加到協(xié)議擴(kuò)展中的屬性和方法對于所有遵守該協(xié)議的類型來說都是可訪問的。
extension ExerciseType {
var caloriesBurnedPerMinutes: Double {
return caloriesBurned / minutes
}
var description: String {
return "Exercise(\(name), burned \(caloriesBurned) calories in \(minutes) minutes)"
}
}
print(ellipticalWorkout.caloriesBurnedPerMinutes)
print(runningWorkout.caloriesBurnedPerMinutes)
協(xié)議擴(kuò)展 -- where 從句
擴(kuò)展允許你為任意類型添加屬性和方法, 不僅僅是你定義過的自定義類型
同樣地, 協(xié)議擴(kuò)展允許你給任何協(xié)議添加新的方法和屬性, 然而, 就像我們上面提到的, 你為協(xié)議添加的屬性和方法只能使用其它確定會存在的屬性和方法。
使用 where 從句來為 SquenceTypes 協(xié)議擴(kuò)展添加元素(Element)類型為指定類型的約束
extension SequenceType where Generator.Element == ExerciseType {
func totalCaloriesBurned() -> Double {
var total: Double = 0
for exercise in self {
total += exercise.caloriesBurned
}
return total
}
}
協(xié)議擴(kuò)展中的where 從句和泛型中的 where 從句的語法一樣。你添加了一個 totalCaloriesBurned() 方法來計算序列所有運(yùn)動(exercise)的卡路里總數(shù)。 在實現(xiàn)中, 你遍歷 self 中的每個 exercise, 這是被允許的, 因為 self 就是某種 SequenceType。
然后你訪問每個元素中的 caloriesBurned 屬性, 這是被允許的, 因為 where 從句約束這個序列的方法的元素類型是 ExerciseType
創(chuàng)建一個ExerciseTypes類型的數(shù)組, 數(shù)組遵守 SequenceType 協(xié)議。所以你可以調(diào)用你的新的 totalCaloriesBurned() 方法。
let mondayWorkout: [ExerciseType] = [ellipticalWorkout, runningWorkout]
print(mondayWorkout.totalCaloriesBurned())
這個數(shù)組能訪問 totalCaloriesBurned() 方法, 因為它的元素類型是[ExerciseType], 所以你得到結(jié)果為 685.0。 如果你創(chuàng)建了一個 [Int] 類型的數(shù)組, 那么 totalCaloriesBurned() 方法是訪問不到的。它不會出現(xiàn)在 Xcode 的自動補(bǔ)全中, 即使你手動輸入, 程序也會爆出編譯錯誤。因為它滿足不了 where 從句要求的數(shù)組的元素類型必須為 ExerciseType 的約束。
協(xié)議擴(kuò)展的默認(rèn)實現(xiàn)
print(ellipticalWorkout)
print(runningWorkout)
當(dāng)一個協(xié)議為它的(某些或全部)屬性和方法提供了默認(rèn)的實現(xiàn)時, 遵守該協(xié)議的類型不一定要實現(xiàn)它們。但是它們能選擇去實現(xiàn)它們?nèi)绻J(rèn)實現(xiàn)不合適的話。
既然 Treadmill 自身實現(xiàn)了 description , 你應(yīng)該看見默認(rèn)的實現(xiàn)只出現(xiàn)在打印 ellpticalWorkout 上了.