# swift - extension

擴(kuò)展可以給一個(gè)現(xiàn)有的類,結(jié)構(gòu)體,枚舉,還有協(xié)議添加新的功能。它還擁有不需要訪問被擴(kuò)展類型源代碼就能完成擴(kuò)展的能力(即逆向建模)。擴(kuò)展和 Objective-C 的分類很相似。(與 Objective-C 分類不同的是,Swift 擴(kuò)展是沒有名字的。)

Extensions 的作用

  1. Add computed instance properties and computed type properties
    添加計(jì)算型實(shí)例屬性和計(jì)算型類屬性
  2. Define instance methods and type methods
    定義實(shí)例方法和類方法
  3. Provide new initializers
    提供新的構(gòu)造器
  4. Define subscripts
    定義下標(biāo)
  5. Define and use new nested types
    定義和使用新的嵌套類型
  6. Make an existing type conform to a protocol
    使已經(jīng)存在的類型遵循(conform)一個(gè)協(xié)議

擴(kuò)展可以給一個(gè)類型添加新的功能,但是不能重寫已經(jīng)存在的功能。

語法使用
1、使用 extension 關(guān)鍵字聲明擴(kuò)展

 extension SomeType {
  // 在這里給 SomeType 添加新的功能
}

2、擴(kuò)展可以擴(kuò)充一個(gè)現(xiàn)有的類型,給它添加一個(gè)或多個(gè)協(xié)議。協(xié)議名稱的寫法和類或者結(jié)構(gòu)體一樣:

extension SomeType: SomeProtocol, AnotherProtocol {
  // 協(xié)議所需要的實(shí)現(xiàn)寫在這里
}

3、計(jì)算型屬性
擴(kuò)展可以給現(xiàn)有類型添加計(jì)算型實(shí)例屬性和計(jì)算型類屬性。這個(gè)例子給 Swift 內(nèi)建的 Double 類型添加了五個(gè)計(jì)算型實(shí)例屬性,從而提供與距離單位相關(guān)工作的基本支持

extension Double {
 var km: Double { return self * 1_000.0 }
 var m: Double { return self }
 var cm: Double { return self / 100.0 }
 var mm: Double { return self / 1_000.0 }
 var ft: Double { return self / 3.28084 }
}
let oneInch = 25.4.mm
print("One inch is \(oneInch) meters")
// 打印“One inch is 0.0254 meters”
let threeFeet = 3.ft
print("Three feet is \(threeFeet) meters")
// 打印“Three feet is 0.914399970739201 meters”

4、構(gòu)造器
擴(kuò)展可以給現(xiàn)有的類型添加新的構(gòu)造器。它使你可以把自定義類型作為參數(shù)來供其他類型的構(gòu)造器使用,或者在類型的原始實(shí)現(xiàn)上添加額外的構(gòu)造選項(xiàng)。

擴(kuò)展可以給一個(gè)類添加新的便利構(gòu)造器,但是它們不能給類添加新的指定構(gòu)造器或者析構(gòu)器。指定構(gòu)造器和析構(gòu)器必須始終由類的原始實(shí)現(xiàn)提供。

如果你使用擴(kuò)展給一個(gè)值類型添加構(gòu)造器,而這個(gè)值類型已經(jīng)為所有存儲屬性提供默認(rèn)值,且沒有定義任何自定義構(gòu)造器,那么你可以在該值類型擴(kuò)展的構(gòu)造器中使用默認(rèn)構(gòu)造器和成員構(gòu)造器。如果你已經(jīng)將構(gòu)造器寫在值類型的原始實(shí)現(xiàn)中,則不適用于這種情況,如同 值類型的構(gòu)造器委托 中所描述的那樣。

如果你使用擴(kuò)展給另一個(gè)模塊中定義的結(jié)構(gòu)體添加構(gòu)造器,那么新的構(gòu)造器直到定義模塊中使用一個(gè)構(gòu)造器之前,不能訪問 self。

在下面的例子中,自定義了一個(gè)的 Rect 結(jié)構(gòu)體用來表示一個(gè)幾何矩形。這個(gè)例子中還定義了兩個(gè)給予支持的結(jié)構(gòu)體 Size 和 Point,它們都把屬性的默認(rèn)值設(shè)置為 0.0:

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()
}

因?yàn)?Rect 結(jié)構(gòu)體給所有的屬性都提供了默認(rèn)值,所以它自動(dòng)獲得了一個(gè)默認(rèn)構(gòu)造器和一個(gè)成員構(gòu)造器,就像 默認(rèn)構(gòu)造器 中描述的一樣。這些構(gòu)造器可以用來創(chuàng)建新的 Rect 實(shí)例:

let defaultRect = Rect()
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
   size: Size(width: 5.0, height: 5.0))

你可以通過擴(kuò)展 Rect 結(jié)構(gòu)體來提供一個(gè)允許指定 point 和 size 的構(gòu)造器:

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)
    }
}

這個(gè)新的構(gòu)造器首先根據(jù)提供的 center 和 size 計(jì)算一個(gè)適當(dāng)?shù)脑c(diǎn)。然后這個(gè)構(gòu)造器調(diào)用結(jié)構(gòu)體自帶的成員構(gòu)造器 init(origin:size:),它會將新的 origin 和 size 值儲存在適當(dāng)?shù)膶傩灾校?/p>

let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
                      size: Size(width: 3.0, height: 3.0))
// centerRect 的 origin 是 (2.5, 2.5) 并且它的 size 是 (3.0, 3.0)

注意

如果你通過擴(kuò)展提供一個(gè)新的構(gòu)造器,你有責(zé)任確保每個(gè)通過該構(gòu)造器創(chuàng)建的實(shí)例都是初始化完整的。

方法
擴(kuò)展可以給現(xiàn)有類型添加新的實(shí)例方法和類方法。在下面的例子中,給 Int 類型添加了一個(gè)新的實(shí)例方法叫做 repetitions:

extension Int {
    func repetitions(task: () -> Void) {
        for _ in 0..<self {
            task()
        }
    }
}

repetitions(task:) 方法僅接收一個(gè) () -> Void 類型的參數(shù),它表示一個(gè)沒有參數(shù)沒有返回值的方法。

定義了這個(gè)擴(kuò)展之后,你可以對任意整形數(shù)值調(diào)用 repetitions(task:) 方法,來執(zhí)行對應(yīng)次數(shù)的任務(wù):

repetitions {
    print("Hello!")
}
// Hello!
// Hello!
// Hello!

可變實(shí)例方法
通過擴(kuò)展添加的實(shí)例方法同樣也可以修改(或 mutating(改變))實(shí)例本身。結(jié)構(gòu)體和枚舉的方法,若是可以修改 self 或者它自己的屬性,則必須將這個(gè)實(shí)例方法標(biāo)記為 mutating,就像是改變了方法的原始實(shí)現(xiàn)。

在下面的例子中,對 Swift 的 Int 類型添加了一個(gè)新的 mutating 方法,叫做 square,它將原始值求平方:

extension Int {
    mutating func square() {
        self = self * self
    }
}
var someInt = 3
someInt.square()
// someInt 現(xiàn)在是 9

下標(biāo)
擴(kuò)展可以給現(xiàn)有的類型添加新的下標(biāo)。下面的例子中,對 Swift 的 Int 類型添加了一個(gè)整數(shù)類型的下標(biāo)。下標(biāo) [n] 從數(shù)字右側(cè)開始,返回小數(shù)點(diǎn)后的第 n 位:

123456789[0] 返回 9

123456789[1] 返回 8

……以此類推:

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
746381295[2]
// 返回 2
746381295[8]
// 返回 7

如果操作的 Int 值沒有足夠的位數(shù)滿足所請求的下標(biāo),那么下標(biāo)的現(xiàn)實(shí)將返回 0,將好像在數(shù)字的左邊補(bǔ)上了 0:

746381295[9]
// 返回 0,就好像你進(jìn)行了這個(gè)請求:
0746381295[9]
嵌套類型
擴(kuò)展可以給現(xiàn)有的類,結(jié)構(gòu)體,還有枚舉添加新的嵌套類型:

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
        }
    }
}

這個(gè)例子給 Int 添加了一個(gè)新的嵌套枚舉。這個(gè)枚舉叫做 Kind,表示特定整數(shù)所代表的數(shù)字類型。具體來說,它表示數(shù)字是負(fù)的、零的還是正的。

這個(gè)例子同樣給 Int 添加了一個(gè)新的計(jì)算型實(shí)例屬性,叫做 kind,它返回被操作整數(shù)所對應(yīng)的 Kind 枚舉 case 分支。

現(xiàn)在,任意 Int 的值都可以使用這個(gè)嵌套類型:

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])
// 打印“+ + - 0 - 0 + ”

方法 printIntegerKinds(_:),使用一個(gè) Int 類型的數(shù)組作為輸入,然后依次迭代這些值。對于數(shù)組中的每一個(gè)整數(shù),方法會檢查它的 kind 計(jì)算型屬性,然后打印適當(dāng)?shù)拿枋觥?/p>

  • 注意 number.kind 已經(jīng)被認(rèn)為是 Int.Kind 類型。所以,在 switch 語句中所有的 Int.Kind case 分支可以被縮寫,就像使用 .negative 替代 Int.Kind.negative.。

存儲屬性和計(jì)算屬性比較
1 .計(jì)算屬性可以用于類、結(jié)構(gòu)體和枚舉,存儲屬性只能用于類和結(jié)構(gòu)體。
2.存儲屬性可以是變量存儲屬性(用關(guān)鍵字 var 定義),也可以是常量存儲屬性(用關(guān)鍵字 let 定義)。計(jì)算屬性只能(用關(guān)鍵字 var 定義)。
3.計(jì)算屬性不直接存儲值,而是提供一個(gè) getter 和一個(gè)可 選的 setter,來間接獲取和設(shè)置其他屬性或變量的值。
4.可以為除了延遲存儲屬性之外的其他存儲屬性添加屬性觀察器,也可以通過重寫屬性的方式為繼承的屬性(包括 存儲屬性和計(jì)算屬性)添加屬性觀察器。你不必為非重寫的計(jì)算屬性添加屬性觀察器,因?yàn)榭梢酝ㄟ^它的 setter 直接監(jiān)控和響應(yīng)值的變化。

參考:
swift-book
Swift計(jì)算屬性和存儲屬性

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容