Swift學習之路-Extension

godiscoder發表於2017-01-11

本文首發地址
請在閱讀本文章時,順手將文中的示例程式碼在playground中敲一遍,這樣能加深理解!!!
閱讀該文章大約需要:15分鐘
讀完之後你能獲得:
1、Extension是什麼
2、它能做什麼

本文全部內容基於Swift版本:3.0.1

Extension的基本語法

extension SomeType {
    // new functionality to add to SomeType goes here
}複製程式碼

Tip:擴充套件可以為一個型別新增新的功能,但是不能重寫已有的功能。

struct Student {
    var name = ""
    var age = 1
    func print() {

    }
}

extension Student {
    //改行會報錯`invalid redeclaration print()`重複宣告print(),重寫變數也是不行的。
    func print() {
    }
}複製程式碼

Swift中的Extension可以做什麼

我們想知道Extension在Swift中能做些什麼,最直接的方法就是檢視Swift的官方文件了。下面是文件中指出Extension能做的六個方面。

  • 新增計算型例項屬性和計算型型別屬性
  • 定義例項方法和型別方法
  • 提供新的構造器
  • 定義下標
  • 定義和使用新的巢狀型別
  • 使已存在的型別遵守某個協議

看完上面Extension能做的六個方面,我們來逐條解釋說明一下:

新增計算型例項屬性和計算型型別屬性

首先我們要了解什麼是計算型屬性,計算型屬性(computed property)不直接儲存值,而是提供一個getter和一個可選的setter,來間接獲取和設定其他屬性或變數的值。關於更多的計算型屬性的內容請自行檢視官方文件,在此不再贅述。那麼我們什麼場景可以用到這個功能呢?舉一個最常見的例子,當你想訪問某個view的width的時候,通常情況下你會這麼寫:

view.frame.size.width複製程式碼

但是這樣寫很長很不方便,作為一個懶惰的程式設計師,這時候你就要想我能不能縮短訪問該屬性的程式碼。不要猶豫了騷年,這時候你只需要寫一個UIView的Extension就可以達到你的目的。

extension UIView {
    var x: CGFloat {
        set {
            self.frame.origin.x = newValue
        }
        get {
            return self.frame.origin.x
        }
    }

    var y: CGFloat {
        set {
            self.frame.origin.y = newValue
        }
        get {
            return self.frame.origin.y
        }
    }

    var width: CGFloat {
        set {
            self.frame.size.width = newValue
        }
        get {
            return self.frame.size.width
        }
    }
    var height: CGFloat {
        set {
            self.frame.size.height = newValue
        }
        get {
            return self.frame.size.height
        }
    }
}複製程式碼

這樣你就可以通過來訪問該屬性。怎麼樣,有沒有感受到Extension的便利之處。

view.width複製程式碼

定義例項方法和型別方法

在你辛辛苦苦寫完一個Student類後,萬惡的產品過來告訴你需求改了,這時雖然你心中有一萬隻草泥馬在奔騰,但是為了心中那份神聖的程式設計師的責任感(當然還有餬口的工資),你還是要修改程式碼。如果你想在不改變原始類的基礎上新增功能,那你可以給Student類新增Extension來解決問題。

Tip:這裡值得注意的一點是在Swift中,Extension可以給類和型別新增,比如你也可以給一個struct新增Extension,而在Objective-C中,你只能給類新增Extension。

class Student {
    var name = ""
    var age = 1
}

extension Student {
    func printCurrentStudentName() {
        print(self.name)
    }
}

var jack = Student()
jack.name = "jack"
jack.printCurrentStudentName()複製程式碼

提供新的構造器(Initializers)

最常見的Rect通常由originsize來構造初始化,但是如果在你寫完Rect的定義後,你偏偏想要通過center和size來確定Rect(作為一個程式設計師就要有一種作死的精神),那你就要用Extension來給Rect提供一個新的構造器。關於更多關於構造器的資訊,請參考官方文件

本例子來源官方文件

struct Size {
    var width = 0.0, height = 0.0
}
struct Point {
    var x = 0.0, y = 0.0
}
struct Rect {
    var origin = Point()
    var size = Size()
}

let defaultRect = Rect()
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
                          size: Size(width: 5.0, height: 5.0))複製程式碼

通過Extension來給Rect新增一個新的構造器。

extension Rect {
    init(center: Point, size: Size) {
        let originX = center.x - (size.width / 2)
        let originY = center.y - (size.height / 2)
        self.init(origin: Point(x: originX, y: originY), size: size)
    }
}複製程式碼

這樣你就可以通過新的構造器來初始化Rect。

let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
                      size: Size(width: 3.0, height: 3.0))
// centerRect's origin is (2.5, 2.5) and its size is (3.0, 3.0)複製程式碼

定義下標

通過Swift中的Extension,你可以給已知型別新增下標。例如下面的例子就是給Int型別新增一個下標,該下標表示十進位制數從右向左的第n個數字。

本例子來源官方文件

extension Int {
    subscript(digitIndex: Int) -> Int {
        var decimalBase = 1
        for _ in 0..<digitIndex {
            decimalBase *= 10
        }
        return (self / decimalBase) % 10
    }
}
746381295[0]
// 5
746381295[1]
// 9複製程式碼

定義和使用新的巢狀型別(nest type)

Extensions可以給已知的類、結構體、列舉新增巢狀型別。下面的例子是給Int型別新增一個判斷正負數的Extension,該Extension巢狀一個列舉。

本例子來源官方文件

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

func printIntegerKinds(_ numbers: [Int]) {
    for number in numbers {
        switch number.kind {
        case .negative:
            print("- ", terminator: "")
        case .zero:
            print("0 ", terminator: "")
        case .positive:
            print("+ ", terminator: "")
        }
    }
    print("")
}
printIntegerKinds([3, 19, -27, 0, -6, 0, 7])
// Prints "+ + - 0 - 0 + "複製程式碼

使已存在的型別遵守某個協議

編寫使該型別遵守某個協議的Extension的語法如下:

extension SomeType: SomeProtocol, AnotherProtocol {
    // implementation of protocol requirements goes here
}複製程式碼

示例程式碼:

protocol StudentProtocol {
    var address: String { get }
}

struct Student {
    var name = ""
    var age = 1

}

extension Student: StudentProtocol {
    var address: String {
        return "address"
    }
}

var jack = Student()
jack.address
//輸出 address複製程式碼

若新增Extension的型別已經實現協議中的內容,你可以寫一個空的Extension來遵守協議:

protocol StudentProtocol {
    var address: String { get }
}

struct Student {
    var address: String {
        return "address"
    }
    var name = ""
    var age = 1
}

extension Student: StudentProtocol {}

var jack = Student()
jack.address
//輸出 address複製程式碼

總結

  • Extension可以為一個已有的類、結構體、列舉型別或者協議型別新增新功能。
  • 可以在沒有許可權獲取原始原始碼的情況下擴充套件型別的內容
  • Extendion和Objective-C中的Category類似。(OC中的Category有名字,Swift中的擴充套件沒有名字)

下篇預告:Swift-Protocol

若本文有何錯誤或者不當之處,還望不吝賜教。謝謝!

Swift-Extension的官方文件

相關文章