標籤:自己的 基於 自己實現 選擇 參數 type asa 例子 強制轉換
// 通過擴充添加協議一致性
// 即便無法修改原始碼, 依然可以通過擴充 令已有類型遵循並符合協議, 擴充可以為已有類型添加屬性, 方法, 下標 以及構造器, 因此可以符合協議中的相應要求
// 注意: 通過擴充令已有類型遵循並符合協議時, 該類型的所有勢力也會隨之獲得協議中定義的各項功能
protocol TextRepresentbble{
var textualDescription: String { get }
}
// 可以通過擴充, 令已有的類 Dice 遵循並符合 TextRepresentable 協議:
// extension Dice: TextOutputStream{
// var textualDescription: String{
// return "ddddd"
// }
// }
// 通過擴充遵循並符合協議, 和在原始定義中遵循並符合協議的效果完全相同, 協議名稱寫在類型名之後, 以冒號隔開, 然後在擴充的大括弧內實現協議要求的內容
// 通過擴充遵循協議
// 當一個類型已經符合某個協議中的所有要求, 卻還沒有聲明遵循該協議時, 可以通過空擴充體來遵循該協議:
struct Hamster{
var name: String
var textualDescription: String {
return "A hamster named \(name)"
}
}
extension Hamster: TextRepresentbble{}
// 從現在起, Hamster 的執行個體可以作為 TextRepresentable 類型使用
let simonTheHamster = Hamster.init(name: "Simon")
let somethingTextRepresentable: TextRepresentbble = simonTheHamster
print(somethingTextRepresentable.textualDescription)
// 注意: 即使滿足了協議的所有要求, 類型也不會自動遵循協議, 必須顯式地遵循協議
// 協議類型的集合
// 協議類型可以在數組或字典這樣的集合中使用
let things: [TextRepresentbble] = [simonTheHamster]
// 如下所示, 可以便利 things 數組, 並列印每個元素的文本資訊
for thing in things {
print(thing.textualDescription)
}
// thing 是 TextRepresentbble 類型而不是 Hamster 類型, 即使執行個體在幕後確實是這些類型中的一種, 由於 thing 是 TextRepresentbble 類型, 任何 TextRepresentbble 的執行個體都有一個 textualDescription 屬性, 所以每次迴圈中可以安全地反問 thing.textualDescription
// 協議的繼承
// 協議能夠繼承 一個 或 多個其他協議, 可以在繼承 的協議的基礎上增加新的要求, 協議的繼承文法與 類的繼承很相似, 多個被繼承的協議之間用 (,) 分割
// protocol InheritingProtocol: SomeProtocol, AnotherProtocl{
// 這裡是協議的定義部分
// }
// 如下例所示:
protocol PrettyTextRepresentable: TextRepresentbble{
var prettyTextualDescription: String { get }
}
// 例子中定義了一個新的協議 PrettyTextRepresentable, 它繼承自 TextRepresentable 協議, 任何遵循 PrettyTextRepresentable 洗衣的類型在滿足該協議的要求時, 也必須滿足 TextRepresentbble 協議的要求.
//extension SnakesAndLadders: PrettyTextRepresentable {
// var prettyTextualDescription: String {
// var output = textualDescription + ":\n"
// for index in 1...finalSquare {
// switch board[index] {
// case let ladder where ladder > 0:
// output += "▲ "
// case let snake where snake < 0:
// output += "▼ "
// default:
// output += "○ "
// }
// }
// return output
// }
//}
//上述擴充令 SnakesAndLadders 遵循了 PrettyTextRepresentable 協議,並提供了協議要求的 prettyTextualDescription 屬性。每個 PrettyTextRepresentable 類型同時也是 TextRepresentable 類型,所以在 prettyTextualDescription 的實現中,可以訪問 textualDescription 屬性。
// 類類型專屬協議
// 你可以在協議的繼承列表中, 通過添加 class 關鍵字來限制協議只能被 類類型遵循, 而結構體 或枚舉不能遵循該協議, class 關鍵字必須第一個出現在協議的繼承列表中, 在其他繼承的協議之前:
// protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
// 這裡是類類型專屬協議的定義部分
// }
// 在以上例子中,協議 SomeClassOnlyProtocol 只能被類類型遵循, 如果嘗試讓結構體或枚舉 類型遵循該協議,則會導致編譯錯誤
// 注意: 當協議定義的要求 需要遵循協議的 類型必須是 引用語義 而非 值語義, 應該採用類類型專屬協議
// 協議合成
// 有時候需要同時遵循多個協議, 你可以將多個協議採用 SomeProtocol & AntherProtocol 這樣的格式進行組合, 稱為 協議合成. 你可以羅列任意多個你想要遵循的協議, 以與符號 (&) 分割
protocol Named{
var name: String{ get }
}
protocol Aged{
var age: Int { get }
}
struct Person: Named,Aged{
var name: String
var age: Int
}
func wishHappyBirthday(to celebrator: Named & Aged){
print("Happy birthday , \(celebrator.name), you‘re \(celebrator.age)")
}
let birthdayPerson = Person.init(name: "Malcolm", age: 21)
wishHappyBirthday(to: birthdayPerson)
// Named 協議包含 String 類型的 name 屬性, Aged 協議包含 Int 類型的 age 屬性, person 結構體遵循了這兩個協議
// wishHappyBirthday(to:) 函數的參數 celebrator 的類型為 Named & Aged, 這意味著它不關心參數的具體類型, 只要參數符合這兩個協議即可
// 注意: 協議合成不會產生新的, 永久的協議, 而是將多個協議中要求合成到一個只在局部範圍中有效臨時協議中
// 檢查協議的一致性
// 你可以使用 類型轉化 中 描述的 is 和 as 操作符來檢查協議的一致性, 即是否符合某協議,並且可以指定轉換到指定的協議類型, 檢查很轉化到某個協議類型在文法上和類型的檢查和轉換完全相同:
// is 用來檢查視力是否符合某個協議, 若是返回 true, 否則返回 false
// as? 返回一個可選值, 當執行個體符合某個協議時, 傳回型別為協議類型的可選值, 否則返回 nil
// as! 將執行個體強制向下轉換到某個協議類型, 如果強制轉換失敗, 會引發執行階段錯誤
protocol HasArea{
var area: Double { get }
}
class Circle: HasArea{
let pi = 3.1415927
var radius: Double
var area: Double{
return pi * radius * radius
}
init(radius: Double) {
self.radius = radius
}
}
class Country: HasArea{
var area: Double
init(area: Double) {
self.area = area
}
}
// Circle 類把 area 屬性實現為基於儲存型屬性 radius 的計算型屬性, Country 類則把 area 屬性實現為儲存型屬性, 這兩個類都正確地符合了 HasArea 協議
// 下面是一個未遵循 HasArea 協議的類
class Animal{
var legs: Int
init(legs: Int) {
self.legs = legs
}
}
// Circle. Country ,Animal 並沒有一個共同的基類, 儘管如此, 他們都是類, 他們的執行個體都可以作為 AnyObject 類型的值, 儲存在同一個數組中:
let objects: [AnyObject] = [
Circle.init(radius: 2.0),
Country.init(area: 243_610),
Animal.init(legs: 4)
]
for object in objects {
if let objectWithArea = object as? HasArea {
print("Area is \(objectWithArea.area)")
}else{
print("Something that doesn‘t have an area")
}
}
// 當迭代出的元素符合 HasArea 協議時, 將 as? 操作符返回的可選值通過可選綁定, 綁定到 objectWithArea 常量上, objectWithAera 是 HasArea 協議類型的執行個體, 因此 area 屬性可以被訪問和列印
// objects 數組中的元素的類型並不會因為強轉而丟失類型資訊, 他們仍然是 Circle , Country , Animal 類型, 然而,當他們被賦值給 objectWithArea 常量時, 只被視為 HasArea 類型, 因此只有 area 屬效能被訪問
// 可選的協議要求
// 協議可以定義 可選要求. 遵循協議的類型可以選擇是否實現這些要求, 在協議中使用 optional 關鍵字作為首碼 來定義可選要求, 可選要求用在你需要和 OC 打交道的代碼中, 協議和可選要求都必須帶上 @objc 屬性, 標記 @objc 特性的協議只能被繼承自 OC 類的類 或者 @objc 類遵循, 其他類以及結構體和 枚舉不能遵循這種協議
// 使用可選要求時(例如, 可選的方法或者屬性) , 他們的類型會自動變成可選的, 比如, 一個類型為 (Int) -> String 的方法會變成 ((Int) -> String)?, 需要注意的是整個函數類型是可選的, 而不是傳回值是可選的
// 協議中的可選要求可通過可選鏈式調用來使用, 因為遵循協議的類型可能沒有實現這些可選要求, 類似 someOptionalMethod?(someArgument) 這樣,你可以在可選方法名稱後加上 ? 來調用可選方法。
@objc protocol CounterDataSource{
@objc optional func incrementForCount(count: Int) -> Int
@objc optional var fixedIncrement: Int { get }
}
// 注意: 嚴格來講, CounterDataSource 協議中的方法和屬性都是可選的, 因此遵循協議的類可以不實現這些要求, 儘管技術上允許這麼做, 不過做好不要這麼寫
class Counter{
var count = 0
var dataSource: CounterDataSource?
func increment() {
if let amount = dataSource?.incrementForCount?(count: count) {
count += amount
}else if let amount = dataSource?.fixedIncrement{
count += amount
}
}
}
// 協議擴充
// 協議可以通過擴充來為遵循協議的類型提供屬性, 方法以及下標的實現, 你可以基於協議本身來實現這些功能, 而無需在每個遵循協議的類型中都重複同樣的實現, 也無需使用全域函數
// 例如.
//extension SomeProtocol {
// func randomBool() -> Bool {
// return random() > 0.5
// }
//}
// 通過協議擴充, 所有遵循協議的類型, 都能自動獲得這個擴充所增加的方法實現, 無需任何額外的修改
// 提供預設實現
// 可以通過協議擴充來為協議要求的屬性, 方法以及下標 提供預設的實現, 如果遵循協議的類型為這些要求提供了自己的實現, 那麼這些自訂實現將會替代擴充中的預設實現被使用
// 注意: 通過協議擴充為協議要求提供的預設實現和可選的協議要求不同, 雖然在這兩種情況下, 遵循協議的類都無需自己實現這些要求, 但是通過擴充提供的預設實現可以直接調用, 而無需使用可選鏈式調用
extension PrettyTextRepresentable{
var prettyTextualDescription: String {
return textualDescription
}
}
// 為擴充協議添加限制條件
// 在擴充協議的時候, 可以指定一些限制條件, 只有遵循協議的類型滿足這些限制條件時. 才能獲得協議擴充提供的預設實現, 協議限制條件寫在協議名之後, 使用 whrer 子句來描述,
// 例如,你可以擴充 CollectionType 協議,但是只適用於集合中的元素遵循了 TextRepresentable 協議的情況:
// extension CollectionType where Generator.Element: TextRepresentbble{
// // 擴充協議定義的內容
// }
swift 學習- 25 -- 協議 02