iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 28
0

擴展 Extensions

擴展為現有的 Class、Struct、Enum、Protocol 類型添加新的功能。這也包括了為無訪問權限的源代碼擴展類型的能力(即所謂的逆向建模)。

Swift 中的擴展可以:

  • 添加計算實例屬性和計算類型屬性
  • 定義實例方法和類型方法
  • 提供新的初始化器
  • 定義下標
  • 定義和使用新的嵌套類型
  • 使現有類型符合協議

在Swift中,你甚至可以擴展一個協議,以提供其要求的實現或添加符合類型的附加功能。

擴展可以向一個類型添加新的方法,但是不能重寫已有的方法。


擴展語法

使用 extension 關鍵字來宣告擴展,例如某個類的擴展:

extension SomeType {
    // 添加新功能
}

擴展可以使已有的類型遵循一個或多個協議。在這種情況下,協議名的書寫方式與 class 或 struct 一樣:

extension SomeType: SomeProtocol, AnotherProtocol {
    // 協議要求的執行
}

如果你向已存在的類型添加新功能,新功能會在該類型的所有實例中可用,即使實例在該擴展定義之前就已經創建。


計算屬性

擴展可以向已有的類型添加計算實例屬性和計算類型屬性。我們在 Swift 內建的 Double 類型添加了三個計算實例屬性,以公尺(m)為基準,用來換算距離:

extension Double {
    var km: Double { return self * 1000 }
    var m: Double { return self }
    var cm: Double { return self / 100 }
}

之後可以在宣告後面的值加上 .cm(公分)、 .m(公尺)或是 .km(公里),來表示值得長度單位:

let distance = 25.2345.km
let length = 123.4.cm

印出結果如下:
https://ithelp.ithome.com.tw/upload/images/20180116/20107701VRL8XBgQ7n.png

也可以用下列方式來計算他的加總:

let total = distance + length
let total2 = 123.km + 456.m

印出結果:
https://ithelp.ithome.com.tw/upload/images/20180116/20107701fdBsBlPHfN.png


初始化器

擴展可以對已有的類型添加新的初始化器,這允許你擴展其他類型以使初始化器接收你的自定義類型作為參數,或提供該類型的原始實現中未包含的額外初始化選項。擴展可以為 class 添加新的便捷初始化器,但是不能為 class 添加指定初始化器或反初始化器。指定初始化器和反初始化器必須由原來 class 的實現提供。

如果你使用擴展為一個值類型添加初始化器,且該值類型為其所有儲存的屬性提供默認值,而又不定義任何自定義初始化器時,你可以在你擴展的初始化器中調用該類型默認的初始化器和成員初始化器。

我們下面定義一個 Struct 用來儲存玩家姓名跟金幣,name 預設為空字串,coins 值為 100 ,我們下面使用擴展添加一個初始化器:

struct Player {
    var name = "xxxxxx"
    var coins = 100
}

extension Player {
    init(name: String) {
        self.name = name
    }
}

我們這時可以測試結果:

https://ithelp.ithome.com.tw/upload/images/20180116/20107701mnUW1G0Uap.png

如果你使用擴展提供了一個新的初始化器,你仍應確保每一個實例都在初始化完成時完全初始化。


方法

擴展可以為已有的類型添加新的實例方法和類型方法。我們這邊直接擴展 Swift 中的 String,並根據輸入的 number 重複印出字串:

extension String{
    func repeatPrint(_ number:Int) {
        for _ in 0...number {
            print(self)
        }
    }
}

之後我們與上面的計算屬性相同,可以使用一個 (.) 來調用方法:

"hello".repeatPrint(3)
"IT鐵人說10次".repeatPrint(10)

輸出結果如下:
https://ithelp.ithome.com.tw/upload/images/20180116/20107701j9yVIfDHAG.png


異變實例方法

添加了擴展的實例方法也可以修改(或變異)實例本身。 修改 self 或其屬性的 struct 和 enum 方法必須將實例方法標記為變異,就像從原始實現中改變方法一樣。

下面我們一樣在 String 中擴展一個 hello() 方法 ,使他印出來的字串前面都加上 Hello:

extension String{
    mutating func hello(){
        self = "Hello," + self
    }
}

var name = "Jeremy"
name.hello()

輸出結果如下:
https://ithelp.ithome.com.tw/upload/images/20180116/201077015opDJ9yMe2.png


下標

擴展能為已有的類型添加新的下標。下面的例子為Swift內建的 Int 類型添加了一個整型下標。這個下標 [n] 返回了從右開始第 n 位的十進制數字,我們參考 apple 程式語言中的範例新增一個超出範圍的判斷式:

extension Int {
    subscript(number: Int) -> String {
        var decimal = 1
        for _ in 1..<number {
            decimal  *= 10
        }
        let result = (self / decimal ) % 10
        if result != 0 {
            return "從右開始算第\(number)個數字為\(result)"
        } else {
            return "超出數字範圍"
        }
    }
}

我們將數字經由除以 decimal 後,再取它除以 10 剩下的餘數為我們的結果,如果沒有 % 10 出現的會是 number 從開始向左的數字都會顯示。 我們結果如下:
https://ithelp.ithome.com.tw/upload/images/20180116/20107701DWt9ZBcyYx.png


內嵌類型

這個範例我們為 Int 添加了新的內嵌枚舉類型。這個名為 Kind 的枚舉表示整數的類型。表示這個數字是正數、0或是負數。這個範例還在 Int 中添加了新的計算實例屬性 kind,以返回該整數的合適 Kind 枚舉 case。

extension Int {
    enum Kind {
        case negative, zero, positive
    }
    var kind: Kind {
        switch self {
        case 0:
            return .zero
        case let i where i > 0:
            return .positive
        default:
            return .negative
        }
    }
}

之後我們新增一個 numberCheck 方法來判斷我們這個數字是什麼總類:

func numberCheck(_ number:Int) {
    switch number.kind{
    case .positive:
        print("\(number)是正數!")
    case .zero:
        print("它為的值 0。")
    case .negative:
        print("\(number)是負數!")
    }
}

結果如下:

https://ithelp.ithome.com.tw/upload/images/20180116/20107701ejepC1KSEP.png


上一篇
Day-27 Swift 語法(23) - 類型轉換
下一篇
Day-29 Swift 語法(25) - 協定 Protocol
系列文
Swift 菜鳥的30天30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言