Swift4.2語言指南(二十二) 擴展
擴展為現有的類,結構,枚舉或協議類型添加新功能。這包括擴展您無法訪問原始源代碼的類型的能力(稱為追溯建模)。擴展類似於Objective-C中的類別。(與Objective-C類別不同,Swift擴展沒有名稱。)
Swift中的擴展可以:
- 添加計算實例屬性和計算類型屬性
- 定義實例方法和類型方法
- 提供新的初始化程序
- 定義下標
- 定義和使用新的嵌套類型
- 使現有類型符合協議
在Swift中,您甚至可以擴展協議以提供其需求的實現,或者添加符合類型可以利用的其他功能。有關更多詳細信息,請參閱協議擴展。
註意
擴展可以為類型添加新功能,但它們不能覆蓋現有功能。
擴展語法
使用extension
關鍵字聲明擴展名:
1 extension SomeType { 2 // new functionality to add to SomeType goes here 3 }
擴展可以擴展現有類型以使其采用一個或多個協議。要添加協議一致性,請按照為類或結構編寫協議名稱的方式編寫協議名稱:
1 extension SomeType: SomeProtocol, AnotherProtocol { 2 // implementation of protocol requirements goes here 3 }
在添加協議與擴展的一致性中描述了以這種方式
擴展可用於擴展現有泛型類型,如擴展泛型類型中所述。您還可以擴展泛型類型以有條件地添加功能,如使用Generic Where子句的擴展中所述。
註意
如果定義擴展以向現有類型添加新功能,則新功能將在該類型的所有現有實例上可用,即使它們是在定義擴展之前創建的。
計算屬性
擴展可以將計算實例屬性和計算類型屬性添加到現有類型。此示例向Swift的內置Double
類型添加了五個計算實例屬性,以便為使用距離單位提供基本支持:
1 extension Double { 2 var km: Double { return self * 1_000.0 }3 var m: Double { return self } 4 var cm: Double { return self / 100.0 } 5 var mm: Double { return self / 1_000.0 } 6 var ft: Double { return self / 3.28084 } 7 } 8 let oneInch = 25.4.mm 9 print("One inch is \(oneInch) meters") 10 // Prints "One inch is 0.0254 meters" 11 let threeFeet = 3.ft 12 print("Three feet is \(threeFeet) meters") 13 // Prints "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
接受a的任何地方的數學計算中使用:
1 let aMarathon = 42.km + 195.m 2 print("A marathon is \(aMarathon) meters long") 3 // Prints "A marathon is 42195.0 meters long"
註意
擴展可以添加新的計算屬性,但它們不能添加存儲的屬性,也不能將屬性觀察器添加到現有屬性。
初始化器
擴展可以為現有類型添加新的初始化程序。這使您可以擴展其他類型以接受您自己的自定義類型作為初始化參數,或者提供未包含在類型的原始實現中的其他初始化選項。
擴展可以為類添加新的便利初始化器,但是它們不能向類中添加新的指定初始化器或取消初始化器。指定的初始化程序和取消初始化程序必須始終由原始類實現提供。
如果使用擴展將初始值設定項添加到為其所有存儲屬性提供默認值的值類型,並且未定義任何自定義初始值設定項,則可以在擴展程序的初始值設定項中調用該值類型的默認初始化程序和成員初始值設定項。如果您已將初始化程序編寫為值類型的原始實現的一部分,則不會出現這種情況,如值類型的初始化程序委派中所述。
如果使用擴展將初始化程序添加到在另一個模塊中聲明的結構,則新的初始化self
程序在從定義模塊調用初始化程序之前無法訪問。
下面的示例定義了一個Rect
表示幾何矩形的自定義結構。該示例還定義了兩個稱為支撐結構Size
和Point
,兩者都提供的默認值0.0
對於所有其屬性的:
1 struct Size { 2 var width = 0.0, height = 0.0 3 } 4 struct Point { 5 var x = 0.0, y = 0.0 6 } 7 struct Rect { 8 var origin = Point() 9 var size = Size() 10 }
由於Rect
結構為其所有屬性提供默認值,因此它會自動接收默認初始值設定項和成員初始值設定項,如默認初始值設定項中所述。這些初始值設定項可用於創建新Rect
實例:
1 let defaultRect = Rect() 2 let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0), 3 size: Size(width: 5.0, height: 5.0))
您可以擴展Rect
結構以提供額外的初始化程序,該初始化程序采用特定的中心點和大小:
1 extension Rect { 2 init(center: Point, size: Size) { 3 let originX = center.x - (size.width / 2) 4 let originY = center.y - (size.height / 2) 5 self.init(origin: Point(x: originX, y: originY), size: size) 6 } 7 }
這個新的初始化程序首先根據提供的center
點和size
值計算適當的原點。初始化程序然後調用結構的自動成員初始化程序init(origin:size:)
,它將新的原點和大小值存儲在適當的屬性中:
1 let centerRect = Rect(center: Point(x: 4.0, y: 4.0), 2 size: Size(width: 3.0, height: 3.0)) 3 // centerRect‘s origin is (2.5, 2.5) and its size is (3.0, 3.0)
註意
如果您提供帶有擴展的新初始化程序,則您仍然有責任確保在初始化程序完成後每個實例都已完全初始化。
方法
擴展可以向現有類型添加新的實例方法和類型方法。以下示例添加了一個調用repetitions
該Int
類型的新實例方法:
1 extension Int { 2 func repetitions(task: () -> Void) { 3 for _ in 0..<self { 4 task() 5 } 6 } 7 }
該repetitions(task:)
方法采用類型的單個參數,表示沒有參數且不返回值的函數。() -> Void
定義此擴展後,您可以repetitions(task:)
在任何整數上調用該方法以執行多次任務:
1 3.repetitions { 2 print("Hello!") 3 } 4 // Hello! 5 // Hello! 6 // Hello!
變異實例方法
添加擴展的實例方法也可以修改(或改變)實例本身。修改的結構和枚舉方法self
或其屬性必須將實例方法標記為mutating
,就像從原始實現中改變方法一樣。
下面的例子添加了一個名為square
Swift Int
類型的新變異方法,它將原始值平方:
1 extension Int { 2 mutating func square() { 3 self = self * self 4 } 5 } 6 var someInt = 3 7 someInt.square() 8 // someInt is now 9
標
擴展可以向現有類型添加新下標。此示例向Swift的內置Int
類型添加整數下標。此下標[n]
返回數字n
右側的十進制數字位置:
123456789[0]
回報9
123456789[1]
回報8
…等等:
1 extension Int { 2 subscript(digitIndex: Int) -> Int { 3 var decimalBase = 1 4 for _ in 0..<digitIndex { 5 decimalBase *= 10 6 } 7 return (self / decimalBase) % 10 8 } 9 } 10 746381295[0] 11 // returns 5 12 746381295[1] 13 // returns 9 14 746381295[2] 15 // returns 2 16 746381295[8] 17 // returns 7
如果該Int
值沒有足夠的數字用於請求的索引,則下標實現將返回0
,就好像該數字已用零填充到左側:
1 746381295[9] 2 // returns 0, as if you had requested: 3 0746381295[9]
嵌套類型
擴展可以向現有類,結構和枚舉添加新的嵌套類型
1 extension Int { 2 enum Kind { 3 case negative, zero, positive 4 } 5 var kind: Kind { 6 switch self { 7 case 0: 8 return .zero 9 case let x where x > 0: 10 return .positive 11 default: 12 return .negative 13 } 14 } 15 }
此示例將新的嵌套枚舉添加到Int
。此枚舉稱為Kind
表示特定整數表示的數字種類。具體而言,它表示該數字是負數,零還是正數。
此示例還為Int
調用添加了一個新的計算實例屬性kind
,該屬性返回該Kind
整數的相應枚舉大小寫。
嵌套枚舉現在可以與任何Int
值一起使用:
1 func printIntegerKinds(_ numbers: [Int]) { 2 for number in numbers { 3 switch number.kind { 4 case .negative: 5 print("- ", terminator: "") 6 case .zero: 7 print("0 ", terminator: "") 8 case .positive: 9 print("+ ", terminator: "") 10 } 11 } 12 print("") 13 } 14 printIntegerKinds([3, 19, -27, 0, -6, 0, 7]) 15 // Prints "+ + - 0 - 0 + "
此函數printIntegerKinds(_:)
采用Int
值的輸入數組並依次叠代這些值。對於數組中的每個整數,函數會考慮該kind
整數的計算屬性,並打印相應的描述。
註意
number.kind
已知是類型的Int.Kind
。因此,所有Int.Kind
案例值都可以在switch
語句中以簡寫形式寫入,.negative
而不是Int.Kind.negative
。
Swift4.2語言指南(二十二) 擴展