Swift基礎-16(協議下)

1.通過擴展添加協議的一致性

即便無法修改源代碼,依然可以通過擴展令已有類型遵循并符合協議。擴展可以為已有類型添加屬性、方法、下標以及構造器,因此可以符合協議中的相應要求。

注意
通過擴展令已有類型遵循并符合協議時,該類型的所有實例也會隨之獲得協議中定義的各項功能。

例如下面這個 TextRepresentable 協議,任何想要通過文本表示一些內容的類型都可以實現該協議。這些想要表示的內容可以是實例本身的描述,也可以是實例當前狀態的文本描述:

protocol TextRepresentable {
    var textualDescription: String { get }
}

可以通過擴展,令先前提到的 Dice 類遵循并符合 TextRepresentable 協議:

extension Dice: TextRepresentable {
    var textualDescription: String {
        return "A \(sides)-sided dice"
    }
}

通過擴展遵循并符合協議,和在原始定義中遵循并符合協議的效果完全相同。協議名稱寫在類型名之后,以冒號隔開,然后在擴展的大括號內實現協議要求的內容

2.通過擴展遵循協議

當一個類型已經符合了某個協議中的所有要求,卻還沒有聲明遵循該協議時,可以通過空擴展體的擴展來遵循該協議:

struct Hamster {
    var name: String
    var textualDescription: String {
        return "A hamster named \(name)"
    }
}
extension Hamster: TextRepresentable {}

從現在起,Hamster 的實例可以作為 TextRepresentable 類型使用:

// 結構體類型的成員逐一構造器
let simonTheHamster = Hamster(name: "Simon")
let somethingTextRepresentable: TextRepresentable = simonTheHamster
print(somethingTextRepresentable.textualDescription)
// 打印 “A hamster named Simon
3.協議類型的集合

協議類型可以在數組或者字典這樣的集合中使用,在協議類型提到了這樣的用法。下面的例子創建了一個元素類型為 TextRepresentable 的數組:

let things: [TextRepresentable] = [game, d12, simonTheHamster]

這個數組中都是一些對象,這些對象實現了TextRepresentable協議,在方法實現中,實現自己的方法

for thing in things {
    print(thing.textualDescription)
}
// A game of Snakes and Ladders with 25 squares
// A 12-sided dice
// A hamster named Simon

thingTextRepresentable 類型而不是 Dice,DiceGame,Hamster 等類型,即使實例在幕后確實是這些類型中的一種。由于 thingTextRepresentable類型,任何 TextRepresentable 的實例都有一個 textualDescription 屬性,所以在每次循環中可以安全地訪問 thing.textualDescription。”

4.協議的繼承

協議能夠繼承一個或多個其他協議,可以在繼承的協議的基礎上增加新的要求。協議的繼承語法與類的繼承相似,多個被繼承的協議間用逗號分隔:

protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
    // 這里是協議的定義部分
}

如下所示,PrettyTextRepresentable 協議繼承了

 protocol PrettyTextRepresentable: TextRepresentable {
    var prettyTextualDescription: String { get }
}

擴展 SnakesAndLadders,使其遵循并符合 PrettyTextRepresentable 協議:

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
    }
}
5.類類型專屬協議

你可以在協議的繼承列表中,通過添加class關鍵字來限制協議只能被類類型遵循,而結構體或枚舉不能遵循該協議。class關鍵字必須第一個出現在協議的繼承列表中,在其他繼承的協議之前:

protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
    // 這里是類類型專屬協議的定義部分
}

在以上例子中,協議SomeClassOnlyProtocol只能被類類型遵循。如果嘗試讓結構體或枚舉類型遵循該協議,則會導致編譯錯誤。

6.檢查協議一致性

你可以使用類型轉換中描述的 is 和 as 操作符來檢查協議一致性,即是否符合某協議,并且可以轉換到指定的協議類型。檢查和轉換到某個協議類型在語法上和類型的檢查和轉換完全相同:

  • is 用來檢查實例是否符合某個協議,若符合則返回 true,否則返回 false。
  • as? 返回一個可選值,當實例符合某個協議時,返回類型為協議類型的可選值,否則返回 nil。
  • as! 將實例強制向下轉換到某個協議類型,如果強轉失敗,會引發運行時錯誤。”

下面的例子定義了一個 HasArea 協議,該協議定義了一個Double 類型的可讀屬性area:

protocol HasArea {
    var area: Double { get }
}

如下所示,Circle 類和 Country 類都遵循了 HasArea 協議:

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協議。

如下所示,Animal 是一個未遵循HasArea 協議的類:

class Animal {
    var legs: Int
    init(legs: Int) { self.legs = legs }
}

Circle,Country,Animal并沒有一個共同的基類,盡管如此,它們都是類,它們的實例都可以作為 AnyObject類型的值,存儲在同一個數組中:

let objects: [AnyObject] = [
Circle(radius: 2.0),
Country(area: 243_610),
Animal(legs: 4)
]
objects數組使用字面量初始化,數組包含一個radius為 2 的 Circle 的實例,一個保存了英國國土面積的 Country 實例和一個 legs 為 4 的 Animal 實例。

如下所示,objects數組可以被迭代,并對迭代出的每一個元素進行檢查,看它是否符合HasArea 協議:

for object in objects {
    if let objectWithArea = object as? HasArea {
        print("Area is \(objectWithArea.area)")
    } else {
        print("Something that doesn't have an area")
    }
}
// Area is 12.5663708
// Area is 243610.0
// Something that doesn't have an area
6.可選的協議要求

協議可以定義可選要求,遵循協議的類型可以選擇是否實現這些要求。在協議中使用optional 關鍵字作為前綴來定義可選要求。可選要求用在你需要和 Objective-C 打交道的代碼中。協議和可選要求都必須帶上@objc屬性。標記 @objc特性的協議只能被繼承自Objective-C類的類或者 @objc 類遵循,其他類以及結構體和枚舉均不能遵循這種協議。”

使用可選要求時(例如,可選的方法或者屬性),它們的類型會自動變成可選的。比如,一個類型為 (Int) -> String 的方法會變成((Int) -> String)?。需要注意的是整個函數類型是可選的,而不是函數的返回值。

@objc protocol CounterDataSource {
    optional func incrementForCount(count: Int) -> Int
    optional var fixedIncrement: Int { get }
}

CounterDataSource 協議定義了一個可選方法 increment(forCount:) 和一個可選屬性 fiexdIncrement,它們使用了不同的方法來從數據源中獲取適當的增量值。

注意
嚴格來講,CounterDataSource 協議中的方法和屬性都是可選的,因此遵循協議的類可以不實現這些要求,盡管技術上允許這樣做,不過最好不要這樣寫。

@objc class TowardsZeroSource: NSObject, CounterDataSource {
    func increment(forCount count: Int) -> Int {
        if count == 0 {
            return 0
        } else if count < 0 {
            return 1
        } else {
            return -1
        }
    }
}
7.協議擴展

協議可以通過擴展來為遵循協議的類型提供屬性、方法以及下標的實現。通過這種方式,你可以基于協議本身來實現這些功能,而無需在每個遵循協議的類型中都重復同樣的實現,也無需使用全局函數。

例如,可以擴展 RandomNumberGenerator 協議來提供 randomBool()方法。該方法使用協議中定義的 random()方法來返回一個隨機的Bool值:

extension RandomNumberGenerator {
    func randomBool() -> Bool {
        return random() > 0.5
    }
}

通過協議擴展,所有遵循協議的類型,都能自動獲得這個擴展所增加的方法實現,無需任何額外修改:

let generator = LinearCongruentialGenerator()
print("Here's a random number: \(generator.random())")
// 打印 “Here's a random number: 0.37464991998171”
print("And here's a random Boolean: \(generator.randomBool())")
// 打印 “And here's a random Boolean: true”
  • 提供默認實現

可以通過協議擴展來為協議要求的屬性、方法以及下標提供默認的實現。如果遵循協議的類型為這些要求提供了自己的實現,那么這些自定義實現將會替代擴展中的默認實現被使用。

注意
通過協議擴展為協議要求提供的默認實現和可選的協議要求不同。雖然在這兩種情況下,遵循協議的類型都無需自己實現這些要求,但是通過擴展提供的默認實現可以直接調用,而無需使用可選鏈式調用。

例如,PrettyTextRepresentable 協議繼承自 TextRepresentable 協議,可以為其提供一個默認的 prettyTextualDescription 屬性,只是簡單地返回textualDescription 屬性的值:

extension PrettyTextRepresentable  {
    var prettyTextualDescription: String {
        return textualDescription
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容