# 擴展
*擴展*可以給一個現有的類,結構體,枚舉,還有協議添加新的功能。它還擁有不需要訪問被擴展類型源代碼就能完成擴展的能力(即*逆向建模*)。擴展和 Objective-C 的分類很相似。(與 Objective-C 分類不同的是,Swift 擴展是沒有名字的。)
Swift 中的擴展可以:
- 添加計算型實例屬性和計算型類屬性
- 定義實例方法和類方法
- 提供新的構造器
- 定義下標
- 定義和使用新的嵌套類型
- 使已經存在的類型遵循(conform)一個協議
在 Swift 中,你甚至可以擴展協議以提供其需要的實現,或者添加額外功能給遵循的類型所使用。你可以從 [協議擴展](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html#ID521) 獲取更多細節。
> 注意
>
> 擴展可以給一個類型添加新的功能,但是不能重寫已經存在的功能。
## 擴展的語法 {#extension-syntax}
使用 `extension` 關鍵字聲明擴展:
```swift
extension SomeType {
// 在這里給 SomeType 添加新的功能
}
```
擴展可以擴充一個現有的類型,給它添加一個或多個協議。協議名稱的寫法和類或者結構體一樣:
```swift
extension SomeType: SomeProtocol, AnotherProtocol {
// 協議所需要的實現寫在這里
}
```
這種遵循協議的方式在 [使用擴展遵循協議](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html#ID277) 中有描述。
擴展可以使用在現有范型類型上,就像 [擴展范型類型](https://docs.swift.org/swift-book/LanguageGuide/Generics.html#ID185) 中描述的一樣。你還可以使用擴展給泛型類型有條件的添加功能,就像 [擴展一個帶有 Where 字句的范型](https://docs.swift.org/swift-book/LanguageGuide/Generics.html#ID553) 中描述的一樣。
> 注意
>
> 對一個現有的類型,如果你定義了一個擴展來添加新的功能,那么這個類型的所有實例都可以使用這個新功能,包括那些在擴展定義之前就存在的實例。
## 計算型屬性 {#computed-properties}
擴展可以給現有類型添加計算型實例屬性和計算型類屬性。這個例子給 Swift 內建的 `Double` 類型添加了五個計算型實例屬性,從而提供與距離單位相關工作的基本支持:
```swift
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”
```
這些計算型屬性表示的含義是把一個 `Double` 值看作是某單位下的長度值。即使它們被實現為計算型屬性,但這些屬性的名字仍可緊接一個浮點型字面值,從而通過點語法來使用,并以此實現距離轉換。
在上述例子中,`Double` 類型的 `1.0` 代表的是“一米”。這就是為什么計算型屬性 `m` 返回的是 `self`——表達式 `1.m` 被認為是計算一個 `Double` 類型的 `1.0`。
其它單位則需要一些單位換算。一千米等于 1,000 米,所以計算型屬性 `km` 要把值乘以 `1_000.00` 來實現千米到米的單位換算。類似地,一米有 3.28084 英尺,所以計算型屬性 `ft` 要把對應的 `Double` 值除以 `3.28084`,來實現英尺到米的單位換算。
這些屬性都是只讀的計算型屬性,所以為了簡便,它們的表達式里面都不包含 `get` 關鍵字。它們使用 `Double` 作為返回值類型,并可用于所有接受 `Double` 類型的數學計算中:
```swift
let aMarathon = 42.km + 195.m
print("A marathon is \(aMarathon) meters long")
// 打印“A marathon is 42195.0 meters long”
```
> 注意
>
> 擴展可以添加新的計算屬性,但是它們不能添加存儲屬性,或向現有的屬性添加屬性觀察者。
## 構造器 {#initializers}
擴展可以給現有的類型添加新的構造器。它使你可以把自定義類型作為參數來供其他類型的構造器使用,或者在類型的原始實現上添加額外的構造選項。
擴展可以給一個類添加新的便利構造器,但是它們不能給類添加新的指定構造器或者析構器。指定構造器和析構器必須始終由類的原始實現提供。
如果你使用擴展給一個值類型添加構造器,而這個值類型已經為所有存儲屬性提供默認值,且沒有定義任何自定義構造器,那么你可以在該值類型擴展的構造器中使用默認構造器和成員構造器。如果你已經將構造器寫在值類型的原始實現中,則不適用于這種情況,如同 [值類型的構造器委托](https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID215) 中所描述的那樣。
如果你使用擴展給另一個模塊中定義的結構體添加構造器,那么新的構造器直到定義模塊中使用一個構造器之前,不能訪問 `self`。
在下面的例子中,自定義了一個的 `Rect` 結構體用來表示一個幾何矩形。這個例子中還定義了兩個給予支持的結構體 `Size` 和 `Point`,它們都把屬性的默認值設置為 `0.0`:
```swift
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()
}
```
因為 `Rect` 結構體給所有的屬性都提供了默認值,所以它自動獲得了一個默認構造器和一個成員構造器,就像 [默認構造器](https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID213) 中描述的一樣。這些構造器可以用來創建新的 `Rect` 實例:
```swift
let defaultRect = Rect()
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
size: Size(width: 5.0, height: 5.0))
```
你可以通過擴展 `Rect` 結構體來提供一個允許指定 point 和 size 的構造器:
```swift
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)
}
}
```
這個新的構造器首先根據提供的 `center` 和 `size` 計算一個適當的原點。然后這個構造器調用結構體自帶的成員構造器 `init(origin:size:)`,它會將新的 origin 和 size 值儲存在適當的屬性中:
```swift
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)
```
> 注意
>
> 如果你通過擴展提供一個新的構造器,你有責任確保每個通過該構造器創建的實例都是初始化完整的。
## 方法 {#methods}
擴展可以給現有類型添加新的實例方法和類方法。在下面的例子中,給 `Int` 類型添加了一個新的實例方法叫做 `repetitions`:
```swift
extension Int {
func repetitions(task: () -> Void) {
for _ in 0..<self {
task()
}
}
}
```
`repetitions(task:)` 方法僅接收一個 `() -> Void` 類型的參數,它表示一個沒有參數沒有返回值的方法。
定義了這個擴展之后,你可以對任意整形數值調用 `repetitions(task:)` 方法,來執行對應次數的任務:
```swift
3.repetitions {
print("Hello!")
}
// Hello!
// Hello!
// Hello!
```
### 可變實例方法 {#mutating-instance-methods}
通過擴展添加的實例方法同樣也可以修改(或 *mutating(改變)*)實例本身。結構體和枚舉的方法,若是可以修改 `self` 或者它自己的屬性,則必須將這個實例方法標記為 `mutating`,就像是改變了方法的原始實現。
在下面的例子中,對 Swift 的 `Int` 類型添加了一個新的 mutating 方法,叫做 `square`,它將原始值求平方:
```swift
extension Int {
mutating func square() {
self = self * self
}
}
var someInt = 3
someInt.square()
// someInt 現在是 9
```
## 下標 {#subscripts}
擴展可以給現有的類型添加新的下標。下面的例子中,對 Swift 的 `Int` 類型添加了一個整數類型的下標。下標 `[n]` 從數字右側開始,返回小數點后的第 `n` 位:
- `123456789[0]` 返回 `9`
- `123456789[1]` 返回 `8`
……以此類推:
```swift
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` 值沒有足夠的位數滿足所請求的下標,那么下標的現實將返回 `0`,將好像在數字的左邊補上了 0:
```swift
746381295[9]
// 返回 0,就好像你進行了這個請求:
0746381295[9]
```
## 嵌套類型 {#nested-yypes}
擴展可以給現有的類,結構體,還有枚舉添加新的嵌套類型:
```swift
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
}
}
}
```
這個例子給 `Int` 添加了一個新的嵌套枚舉。這個枚舉叫做 `Kind`,表示特定整數所代表的數字類型。具體來說,它表示數字是負的、零的還是正的。
這個例子同樣給 `Int` 添加了一個新的計算型實例屬性,叫做 `kind`,它返回被操作整數所對應的 `Kind` 枚舉 case 分支。
現在,任意 `Int` 的值都可以使用這個嵌套類型:
```swift
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(_:)`,使用一個 `Int` 類型的數組作為輸入,然后依次迭代這些值。對于數組中的每一個整數,方法會檢查它的 `kind` 計算型屬性,然后打印適當的描述。
> 注意
>
> `number.kind` 已經被認為是 `Int.Kind` 類型。所以,在 `switch` 語句中所有的 `Int.Kind` case 分支可以被縮寫,就像使用 `.negative` 替代 `Int.Kind.negative.`。
- 1.關于 Swift
- 2.Swift 初見
- 2-1基礎部分
- 2-2基本運算符
- 2-3字符串和字符
- 2-4集合類型
- 2-5控制流
- 2-6函數
- 2-7閉包
- 2-8枚舉
- 2-9類和結構體
- 2-10屬性
- 2-11方法
- 2-12下標
- 2-13繼承
- 2-14構造過程
- 2-15析構過程
- 2-16可選鏈
- 2-17錯誤處理
- 2-18類型轉換
- 2-19嵌套類型
- 2-20擴展
- 2-21協議
- 2-22泛型
- 2-23不透明類型
- 2-24自動引用計數
- 2-25內存安全
- 2-26訪問控制
- 2-27高級運算符
- 3-1關于語言參考
- 3-2詞法結構
- 3-3類型
- 3-4表達式
- 3-5語句
- 3-6聲明
- 3-7特性
- 3-8模式
- 3-9泛型參數
- 4語法總結