Swift vs. Kotlin 漫談系列之接口

Kotlin 君和 Swift 君在一個團隊一塊兒開發已經好久了,因爲平臺的差別性,他們常常會進行一些技術上的交流(PK),「Kotlin vs. Swift」 系列就是他們在互相切磋時的語錄。內容會由簡及深,慢慢深刻。git

技術漫談

Swift:編程

Hi, Kotlin 君, Swift 4 發佈了,咱們今天就基於 Swift 4 的新語法來討論一下接口吧?swift

Kotlin:設計模式

好啊,接口對咱們開發來講是個很重要的概念。設計模式中要求咱們寫代碼要遵循依賴倒置原則,就是程序要依賴於抽象接口,不要依賴於具體實現,也就是要求咱們要面向接口編程。數組

Swift:app

是的,在 Swift 中,接口被稱爲協議(即 Protocol ), 蘋果大大強化了 Protocol 在這門語言中的地位,整個 Swift 標準庫也是基於 Protocol 來設計的,能夠說 Swift 是一門面向 protocol 編程的語言。框架

Kotlin:dom

聽起來好流比,那來講說大家是怎麼定義接口的?ide

Swift:函數

咱們用 Protocol 關鍵字來定義接口:

protocol SomeProtocol {
    func f()
}

大家呢?

Kotlin:

咱們同 Java 同樣,用 interface 關鍵字來定義接口:

interface MyInterface {
    fun f()
}

Swift:

嗯,看起來就是關鍵字不同。大家怎麼實現接口呢?

Kotlin:

一個類要實現某個接口,須要在類型名稱後加上協議名稱,中間以冒號(:)分隔:

class MyClass: MyInterface {
    override fun f() {
       // 具體實現
    }
}

一個類或者對象能夠實現一個或多個接口。實現多個接口時,各接口之間用逗號(,)分隔.

Swift:

咱們也是同樣的,只是咱們不須要寫 override 關鍵字,只有當子類複寫父類的方法或計算屬性時才須要用 override 修飾。另外,咱們還能夠經過擴展類型來實現協議:

class MyClass {
    //...類的定義
}

extension MyClass: SomeProtocol {
    func f() {
        // 具體實現
    }
}

Kotlin:

? ,這意味着大家不用修改原有類型,就可讓原有類型符合某個協議了,甚至能夠擴展標準庫中的某個基礎類型來實現自定義的協議。這很符合開閉原則嘛。

Swift:

是啊,牛不牛 ?。

咱們實現協議的類型除了 class 外,還能夠是 structenum

Kotlin:

Kotlin 沒有結構體的概念, enum 也能夠實現接口。

來講說大家的接口中能夠聲明哪些東西吧?

Swift:

咱們能夠在協議中聲明屬性和方法,用 var 關鍵字來聲明變量屬性,並在屬性聲明後加上 { set get } 來表示屬性是可讀可寫的,用 { get } 來表示屬性是隻讀的。

協議裏面聲明的屬性和方法必定是抽象的,不能有實現,由符合協議的類型來提供全部屬性和方法的實現。

Kotlin:

咱們也能夠聲明屬性和方法,並且 Kotlin 能夠直接在接口中爲屬性和方法提供默認實現:

interface MyInterface {
    val prop: Int // 抽象的
    val propertyWithImplementation: String
        get() = "foo"

    fun foo() {
        print(prop)
    }
}

class MyClass : MyInterface {
    override val prop: Int = 29
}

Swift:

?,雖然咱們不能在協議中直接提供屬性和方法的默認實現,可是咱們能夠經過協議擴展來達到此目的。

protocol MyProtocol {
    var prop: Int { get set }
    var propertyWithImplementation: String { get }
    func foo()
}

extension MyProtocol {
    var propertyWithImplementation: String {
        return "foo"
    }
    
    func foo() {
        print(prop)
    }
}

class MyClass: MyProtocol {
    var prop: Int = 29
}

Kotlin:

哇~,大家這個協議擴展有點厲害了。

Swift:

是的,正是這個特性,才使得咱們面向協議編程成爲可能。咱們甚至能夠在擴展中添加協議裏沒有定義過的方法和屬性。

extension MyProtocol {
    func isExceed() -> Bool {
        return prop > 30
    }
}
let instance = MyClass()
print(instance.isExceed())
// false

Kotlin:

?,這就意味着大家也有能力擴展標準庫裏的協議了,能夠很方便的給標準庫裏的協議添加新的方法和屬性。

Swift:

聰明,確實是這樣。不只如此,在擴展協議的時候,還能夠指定一些限制條件,只有遵循協議的類型知足這些限制條件時,才能得到協議擴展提供的默認實現。

protocol TextRepresentable {
    var textualDescription: String { get }
}

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

extension Collection where Iterator.Element: TextRepresentable {
    var textualDescription: String {
        let itemsAsText = self.map { $0.textualDescription }
        return "[" + itemsAsText.joined(separator: ", ") + "]"
    }
}

let hamsters = [Hamster(name: "Jim"), Hamster(name: "Merry")]
print(hamsters.textualDescription)
// [A hamster named Jim, A hamster named Merry]

這裏擴展了 Swift 標準庫中的 Collection 協議,可是限制只適用於集合中的元素遵循了 TextRepresentable 協議的狀況。 由於 SwiftArray 符合 Collection 協議,而 Hamster 類型又符合 TextRepresentable 協議,因此 hamsters 可使用 textualDescription 屬性獲得數組內容的文本表示。

Kotlin:

贊啊~,大家這個協議擴展太強大了,不只能夠擴展本身定義的協議,還能夠擴展標準庫中的協議,怪不得蘋果稱 Swift 是面向協議編程的語言。

Swift 在實現多個協議時,會不會有不一樣協議帶來同名方法或屬性的衝突的問題?

Swift:

咱們還不能很好地處理多個協議的衝突問題。?

Kotlin:

? Kotlin 能夠,Kotlin 有一套規則來處理這樣的衝突。在 Kotlin 中,若是一個類從它的直接超類繼承相同成員的多個實現(因爲接口函數能夠有實現),它必須覆蓋這個成員並提供其本身的實現。 爲了表示採用從哪一個超類型繼承的實現,咱們使用由尖括號中超類型名限定的 super,如 super

open class A {
    open fun f() { print("A") }
    fun a() { print("a") }
}

interface B {
    fun f() { print("B") } // interface members are 'open' by default
    fun b() { print("b") }
}

class C() : A(), B {
    // The compiler requires f() to be overridden:
    override fun f() {
        super<A>.f() // call to A.f()
        super<B>.f() // call to B.f()
    }
}

Swift:

這個好贊,能夠不怕名字衝突了 ?。

Kotlin 的接口中能夠聲明類方法嗎?

Kotlin:

Kotlin 裏面已經沒有類方法的概念了。

Swift:

咱們能夠在協議中使用 static 關鍵字來聲明類型方法,若是實現該協議的類型是 class 類型,則在實現類中除了用 static 來修飾類型方法外,也可使用 class關鍵字.

protocol SomeProtocol {
    static func someTypeMethod()
}

class SomeClass: SomeProtocol {
    // 這裏也能夠用 static 修飾,區別是 static 修飾的屬性
    // 或方法不能被子類複寫,class 修飾的能夠被子類複寫
    class func someTypeMethod() {
        print("type method")
    }
}

Kotlin:

咱們的接口雖然不支持類方法,可是咱們能夠給接口中定義的方法的參數設置默認值。

Swift:

這。。。咱們不支持爲協議中的方法的參數提供默認值。?

Kotlin:

?,方法參數的默認值必須定義在接口中,在實現類或對象實現該方法時,不能爲函數提供默認值。同時接口的中函數不能用 JVMOverride 註解修飾,因此接口中定義的帶有默認值的參數,不能爲 Java 生成重載方法,若是接口是定義在庫裏面,Kotlin 的實現也沒法使用自動重載功能,須要手動重載。

interface IDownload{
    fun(url: String, isSupportBreakpointC: Boolean = true)
}

class DownloadImpl: IDownload{
    override fun(url: String, isSupportBreakpointC: Boolean){
        
    }
}

Swift:

?,這點算你強。

咱們的協議中能夠定義可變方法,若是協議中定義的實例方法會改變遵循該協議的類型的實例,那麼須要在該方法前加 mutating 關鍵字, 表示能夠在該方法中修改它所屬的實例以及實例的任意屬性的值, 例如:

protocol Togglable {
    mutating func toggle()
}

enum OnOffSwitch: Togglable {
    case off, on
    mutating func toggle() {
        switch self {
        case .off:
            self = .on
        case .on:
            self = .off
        }
    }
}
var lightSwitch = OnOffSwitch.off
lightSwitch.toggle()
// lightSwitch 如今的值爲 .On

Kotlin:

? 咱們沒這特性,這點你贏了。

Swift:

豈止如此,咱們的協議中還能夠要求遵循協議的類型實現指定的構造器:

protocol SomeProtocol {
    init(someParameter: Int)
}

class SomeClass: SomeProtocol {
    required init(someParameter: Int) {
        // initializer implementation goes here
    }
}

在符合協議的類中實現構造器,必須在構造器實現前加上 required 修飾符。使用 required 修飾符能夠確保全部子類也必須提供此構造器實現,從而也能符合協議。 若是類已經被標記爲 final,那麼不須要在協議構造器的實現中使用 required 修飾符,由於 final 類不能有子類.

協議還能夠爲遵循協議的類型定義可失敗構造器。

Kotlin:

好吧,咱們不能夠在接口中聲明構造器。

Swift:

?,大家的接口能夠繼承嗎?

Swift 中協議可以繼承一個或多個其餘協議,能夠在繼承的協議的基礎上增長新的要求.

Kotlin:

固然能夠,這是基本的用法好伐。

Swift:

好吧。。咱們還能夠經過讓協議繼承 AnyObject 協議來限制協議只能被 Class 類型遵循,而結構體或枚舉不能遵循該協議。

Kotlin:

咱們並無這種限制,接口能夠被類和枚舉實現。

Swift:

大家的接口能夠組合嗎?

Swift 能夠採用 & 符號將多個協議進行組合:

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(name: "Malcolm", age: 21)
wishHappyBirthday(to: birthdayPerson)
// Prints "Happy birthday, Malcolm, you're 21!"

這裏 wishHappyBirthday(to:) 函數的參數類型爲 Named & Aged, 這意味着它不關心參數的具體類型,只要參數符合這兩個協議便可。固然也能夠給組合的協議指定一個別名:typealias Property = Named & Aged

Kotlin:

666,大家的協議真是玩出花了,這個功能咱們也沒有?。

Swift:

除了協議與協議組合外,協議還能夠與類進行組合:

class Location {
    var latitude: Double
    var longitude: Double
    init(latitude: Double, longitude: Double) {
        self.latitude = latitude
        self.longitude = longitude
    }
}
class City: Location, Named {
    var name: String
    init(name: String, latitude: Double, longitude: Double) {
        self.name = name
        super.init(latitude: latitude, longitude: longitude)
    }
}
func beginConcert(in location: Location & Named) {
    print("Hello, \(location.name)!")
}
 
let seattle = City(name: "Seattle", latitude: 47.6, longitude: -122.3)
beginConcert(in: seattle)

這裏的 beginConcert(in:) 函數的參數要求是 Location 的子類,且必須符合 Named 協議.

Kotlin:

太讚了~,給你點32個贊?!

Swift:

大家是怎麼判斷某個實例是否符合某個協議的?

Kotlin:

這就是判斷某個對象是不是某個類型嘛,固然是用 is 啦,若是是類型轉換的話用 as

Swift:

嗯嗯,這點咱們是一致的。

大家能夠定義接口中的方法或屬性爲可選嗎?

Kotlin:

何謂可選?

Swift:

就是能夠實現也能夠不實現

Kotlin:

前面講過了啊,若是接口中的屬性或方法在實現類中能夠實現也能夠不實現,則能夠在接口定義中爲該方法提供默認實現。

Swift:

嗯,Swift 是經過協議擴展提供默認實現來到達可選的目的。

不過 Swift 也能夠像 Objective-C 裏那樣定義可選的接口方法,就須要在 protocol 定義以前加上 @objc,將 protocol 變爲 Objective-C 的。而後使用 optional 關鍵字來聲明某些方法或屬性在符合該協議的類中能夠不實現,以下:

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

須要注意的是,標記爲 @objcprotocol 只能被 class 實現,不能被 structenum 類型實現,並且實現它的 class 中的方法也必須被標註爲 @objc,或者整個類就是繼承自 NSObject

Kotlin:

額。。。這豈不是很蛋疼

Swift:

?,是的,因此這種方式並不提倡。

Swift 可使用 Protocol 來實現委託(代理)模式,委託(代理)模式容許類或結構體將一些須要它們負責的功能委託給其餘類型的實例,以下:

protocol RentHouserDelegate{
    func rent(_ name:String);
}

class Tenant {
    var name = "lucy"
    var delegate: RentHouserDelegate?
    func rentHouse(){
        delegate?.rent(name)
    }
}

class Intermediary: RentHouserDelegate {
    var name="lily"
    func rent(_ name:String) {
        print("\(name) 請 \(self.name) 幫她租一套房子");
    }
}

var person = Tenant();
person.delegate = Intermediary()
person.rentHouse()
// lucy 請 lily 幫她租一套房子

Kotlin:

這是接口的一種經常使用方法,咱們依賴注入框架就大量使用這種方式。

Swift:

哈哈,英雄所見略同。

好了,就到這吧,今天的PK互有攻防,好帶勁?~

Kotlin:

?,整體來講仍是大家的協議比較強大。

Swift:

那是,要否則蘋果怎麼敢稱 Swift 是一門面向協議編程的語言呢

Kotlin:

好吧,我們來日方長。

Swift:

嗯嗯,後會有期。

知識點總結

Kotlin

接口定義

同 Java 同樣,Kotlin 用 interface 關鍵字來定義接口,Kotlin 接口中能夠有函數的實現,也能夠只有抽象方法,接口沒法保存狀態,它能夠有屬性但必須聲明爲抽象或提供訪問器實現。

interface MyInterface {
    fun bar()
    fun foo() {
      // 可選的方法體
    }
}

實現接口

Kotlin 的一個類或者對象能夠實現一個或多個接口。因爲 Kotlin 接口自己的函數式能夠有實現的,因此在一個類或對象實現多個接口的時候,就有可能發生衝突,這包括接口之間的的成員衝突,也包括接口與父類直接的成員衝突。

覆蓋衝突

在 Kotlin 中,若是一個類從它的直接超類繼承相同成員的多個實現(因爲接口函數能夠有實現),它必須覆蓋這個成員並提供其本身的實現。 爲了表示採用從哪一個超類型繼承的實現,咱們使用由尖括號中超類型名限定的 super,如 super<Base>。

open class A {
    open fun f() { print("A") }
    fun a() { print("a") }
}

interface B {
    fun f() { print("B") } // interface members are 'open' by default
    fun b() { print("b") }
}

class C() : A(), B {
    // The compiler requires f() to be overridden:
    override fun f() {
        super<A>.f() // call to A.f()
        super<B>.f() // call to B.f()
    }
}

同時繼承 A 和 B 沒問題,而且 a() 和 b() 也沒問題由於 C 只繼承了每一個函數的一個實現。 可是 f() 由 C 繼承了兩個實現,因此咱們必須在 C 中覆蓋 f() 而且提供咱們本身的實現來消除歧義。

接口中的屬性

Kotlin 中能夠在接口中定義屬性。在接口中聲明的屬性要麼是抽象的,要麼提供訪問器的實現。在接口中聲明的屬性不能有幕後字段(backing field),所以接口中聲明的訪問器不能引用它們。

interface MyInterface {
    val prop: Int // 抽象的

    val propertyWithImplementation: String
        get() = "foo"

    fun foo() {
        print(prop)
    }
}

class Child : MyInterface {
    override val prop: Int = 29
}

函數默認參數與重載

若是接口函數須要定義默認值的話,必須定義在接口中,在實現類或對象實現該方法時,不能爲函數提供默認值。同時接口的中函數不能用 JVMOverride 註解修飾,因此接口中定義的帶有默認值的參數,不能爲 Java 生成重載方法,若是接口是定義在庫裏面,Kotlin 的實現也沒法使用自動重載功能,須要手動重載。

interface IDownload{
    fun(url: String, isSupportBreakpointC: Boolean = true)
}

class DownloadImpl{
    override fun(url: String, isSupportBreakpointC: Boolean){
        
    }
}

Swift

Protocol

Swift 是一門支持面向協議編程的語言,在 Swift 語言中,協議被賦予了更多的功能和更廣闊的使用空間。恰逢蘋果發佈了 swift 4,如下都是基於最新的 swift 4 語法進行講述。

協議語法

協議聲明:

protocol SomeProtocol {
    // protocol definition goes here
}

要讓自定義類型符合某個協議,須要在類型名稱後加上協議名稱,中間以冒號(:)分隔。符合多個協議時,各協議之間用逗號(,)分隔. swift 中,符合協議的類型能夠是 classstructenum

struct SomeStructure: FirstProtocol, AnotherProtocol {
    // structure definition goes here
}

須要注意的是,若是某個類在符合某個協議的同時又繼承自某個父類,應將其父類名放在其符合的協議名以前。

協議屬性聲明

協議中能夠聲明符合此協議的類型必須實現的屬性:

protocol SomeProtocol {
    var mustBeSettable: Int { get set }
    var doesNotNeedToBeSettable: Int { get }
}

協議不指定屬性是存儲型屬性仍是計算型屬性,它只指定屬性的名稱和類型,以及屬性是可讀的仍是可讀可寫的。

協議老是用 var 關鍵字來聲明變量屬性,在類型聲明後加上 { set get } 來表示屬性是可讀可寫的,用 { get } 來表示屬性是隻讀的。

協議中老是使用 static 關鍵字定義類型屬性,若是是 class類型實現協議,除了 static,還可使用 class 關鍵字來聲明類型屬性。

protocol AnotherProtocol {
    static var someTypeProperty: Int { get }
}

class SomeClass: AnotherProtocol {
    // 這裏也能夠用 static 修飾,區別是 static 修飾的屬性
    或方法不能被子類複寫,class 修飾的能夠被子類複寫
    class var someTypeProperty: Int {
        return 0
    }
}

協議方法聲明

協議能夠要求遵循協議的類型實現某些指定的實例方法或類方法。須要注意的是,不支持爲協議中的方法的參數提供默認值。

protocol RandomNumberGenerator {
    func random() -> Double
}

與屬性相似,在協議中也使用 static 定義類方法,當 class 類型實現協議時,可使用 class 關鍵字來修飾.

若是協議中定義的實例方法會改變遵循該協議的類型的實例,那麼須要在該方法前加 mutating 關鍵字, 表示能夠在該方法中修改它所屬的實例以及實例的任意屬性的值, 例如:

protocol Togglable {
    mutating func toggle()
}

enum OnOffSwitch: Togglable {
    case off, on
    mutating func toggle() {
        switch self {
        case .off:
            self = .on
        case .on:
            self = .off
        }
    }
}
var lightSwitch = OnOffSwitch.off
lightSwitch.toggle()
// lightSwitch 如今的值爲 .On

協議構造器聲明

協議能夠要求遵循協議的類型實現指定的構造器:

protocol SomeProtocol {
    init(someParameter: Int)
}

class SomeClass: SomeProtocol {
    required init(someParameter: Int) {
        // initializer implementation goes here
    }
}

在符合協議的類中實現構造器,必須在構造器實現前加上 required 修飾符。使用 required 修飾符能夠確保全部子類也必須提供此構造器實現,從而也能符合協議. 若是類已經被標記爲 final,那麼不須要在協議構造器的實現中使用 required 修飾符,由於 final 類不能有子類.

協議還能夠爲遵循協議的類型定義可失敗構造器。

委託(代理)模式

Swift 可使用 Protocol 來實現委託(代理)模式,委託(代理)模式容許類或結構體將一些須要它們負責的功能委託給其餘類型的實例,以下:

protocol RentHouserDelegate{
    func rent(_ name:String);
}

class Tenant {
    var name = "lucy"
    var delegate: RentHouserDelegate?
    func rentHouse(){
        delegate?.rent(name)
    }
}

class Intermediary: RentHouserDelegate {
    var name="lily"
    func rent(_ name:String) {
        print("\(name) 請 \(self.name) 幫她租一套房子");
    }
}

var person = Tenant();
person.delegate = Intermediary()
person.rentHouse()
// lucy 請 lily 幫她租一套房子

經過擴展遵循協議

能夠經過擴展令已有類型遵循並符合協議:

protocol TextRepresentable {
    var textualDescription: String { get }
}

struct Circular {
    var radius: Int
}

extension Circular: TextRepresentable {
    var textualDescription: String {
        return "The circular's radius is \(radius)"
    }
}

let circular = Circular(radius: 2)
print(circular.textualDescription)
// The circular's radius is 2

當一個類型已經符合了某個協議中的全部要求,卻尚未聲明遵循該協議時,能夠經過空擴展來使該類型遵循該協議:

struct Square {
    var width: Int
    var textualDescription: String {
        return "The square's width is \(width)"
    }
}
extension Square: TextRepresentable {}

let square = Square(width: 3)
let squareTextRepresentable: TextRepresentable = square
print(squareTextRepresentable.textualDescription)
// The square's width is 3

協議類型

儘管協議自己並未實現任何功能,可是協議能夠被當作一個成熟的類型來使用。
協議類型也能夠在數組或者字典這樣的集合中使用:

let things: [TextRepresentable] = [circular, square]
for thing in things {
    print(thing.textualDescription)
}

協議的繼承

協議可以繼承一個或多個其餘協議,能夠在繼承的協議的基礎上增長新的要求:

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

extension Square: PrettyTextRepresentable {
    var prettyTextualDescription: String {
        var output = textualDescription + ": "
        output += "the area is \(width*width)"
        return output
    }
}

print(square.prettyTextualDescription)
// The square's width is 3: the area is 9

Class 類型專屬協議

經過讓協議繼承 AnyObject 協議來限制協議只能被 Class 類型遵循,而結構體或枚舉不能遵循該協議

protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol {
    // class-only protocol definition goes here
}

協議合成

能夠採用 & 符號將多個協議進行組合:

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(name: "Malcolm", age: 21)
wishHappyBirthday(to: birthdayPerson)
// Prints "Happy birthday, Malcolm, you're 21!"

這裏 wishHappyBirthday(to:) 函數的參數類型爲 Named & Aged, 這意味着它不關心參數的具體類型,只要參數符合這兩個協議便可。固然也能夠給組合的協議指定一個別名:typealias Property = Named & Aged

除了協議與協議組合外,協議還能夠與 class 進行組合:

class Location {
    var latitude: Double
    var longitude: Double
    init(latitude: Double, longitude: Double) {
        self.latitude = latitude
        self.longitude = longitude
    }
}
class City: Location, Named {
    var name: String
    init(name: String, latitude: Double, longitude: Double) {
        self.name = name
        super.init(latitude: latitude, longitude: longitude)
    }
}
func beginConcert(in location: Location & Named) {
    print("Hello, \(location.name)!")
}
 
let seattle = City(name: "Seattle", latitude: 47.6, longitude: -122.3)
beginConcert(in: seattle)

這裏的 beginConcert(in:) 函數的參數要求是 Location 的子類,且必須符合 Named 協議.

檢查協議一致性

能夠經過 is as? as 來檢查某個實例是否符合某個協議:

let things: [Any] = [circular, square, "abc"]
for thing in things {
    if let object = thing as? TextRepresentable {
        print(object.textualDescription)
    } else {
        print("It does not conform to TextRepresentable")
    }
}

可選協議

原生的 Swift protocol 裏沒有可選項,全部定義的方法都是必須實現的。若是想要像 Objective-C 裏那樣定義可選的接口方法,就須要在 protocol 定義以前加上 @objc,將 protocol 變爲 Objective-C 的。而後使用 optional 關鍵字來聲明某些方法或屬性在符合該協議的類中能夠不實現,以下:

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

須要注意的是,標記爲 @objcprotocol 只能被 class 實現,不能被 structenum 類型實現,並且實現它的 class 中的方法還必須也被標註爲 @objc,或者整個類就是繼承自 NSObject。這仍是很蛋疼的。

協議擴展

協議能夠經過擴展來爲遵循協議的類型提供屬性和方法的實現,即便協議中沒有聲明。這樣就無需在每一個遵循協議的類型中都重複一樣的實現,也無需使用全局函數。

protocol TextRepresentable {
    var textualDescription: String { get }
}

extension TextRepresentable {
    func hasDescription() -> Bool {
        return !textualDescription.isEmpty
    }
}

還能夠經過協議擴展來爲協議要求的屬性、方法提供默認的實現。這樣在遵循這個協議的類型中,能夠不用實現這個屬性或方法,調用的時候默認調 extension 中的實現。這也至關於變相將 protocol 中的屬性或方法設定爲了 `optional.

extension TextRepresentable {
    var textualDescription: String {
        return "This is a shape"
    }
}

在擴展協議的時候,也能夠指定一些限制條件,只有遵循協議的類型知足這些限制條件時,才能得到協議擴展提供的默認實現。

extension Collection where Iterator.Element: TextRepresentable {
    var textualDescription: String {
        let itemsAsText = self.map { $0.textualDescription }
        return "[" + itemsAsText.joined(separator: ", ") + "]"
    }
}

let circulars = [Circular(radius: 1), Circular(radius: 2)]
print(circulars.textualDescription)
// [The circular's radius is 1, The circular's radius is 2]

這裏擴展了 Collection 協議,可是限制只適用於集合中的元素遵循了 TextRepresentable 協議的狀況。 由於 SwiftArray 符合 Collection 協議,而 Circular 類型又符合 TextRepresentable 協議,因此 circulars 可使用 textualDescription 屬性獲得數組內容的文本表示

相關文章
相關標籤/搜索