Extensions (扩展)
Extensionsadd new functionality to an existing class, structure, enumeration, or protocol type. This includes the ability to extend types for which you do not have access to the original source code (known asretroactive modeling). Extensions are similar to categories in Objective-C. (Unlike Objective-C categories, Swift extensions do not have names.)
扩展就是为一个已有的类、结构体、枚举类型或者协议类型添加新功能。这包括在没有权限获取原始源代码的情况下扩展类型的能力(即逆向建模)。扩展和 Objective-C 中的分类类似。(与 Objective-C 不同的是,Swift 的扩展没有名字。)
Extensions in Swift can:
Swift 中的扩展可以:
1. Add computed instance properties and computed type properties
添加计算型属性和计算型类型属性
2. Define instance methods and type methods
定义实例方法和类型方法
3. Provide new initializers
提供新的构造器
4. Define subscripts
定义下标
5. Define and use new nested types
定义和使用新的嵌套类型
6. Make an existing type conform to a protocol
使一个已有类型符合某个协议
In Swift, you can even extend a protocol to provide implementations of its requirements or add additional functionality that conforming types can take advantage of. For more details, seeProtocol Extensions.
在 Swift 中,你甚至可以对协议进行扩展,提供协议要求的实现,或者添加额外的功能,从而可以让符合协议的类型拥有这些功能。你可以从协议扩展获取更多的细节。
NOTE
Extensions can add new functionality to a type, but they cannot override existing functionality.
扩展可以为一个类型添加新的功能,但是不能重写已有的功能。
Extension Syntax (扩展语法)
Declare extensions with theextensionkeyword:
使用关键字extension来声明扩展:
extension SomeType{
// new functionality to add to SomeType goes here
}
An extension can extend an existing type to make it adopt one or more protocols. Where this is the case, the protocol names are written in exactly the same way as for a class or structure:
可以通过扩展来扩展一个已有类型,使其采纳一个或多个协议。在这种情况下,无论是类还是结构体,协议名字的书写方式完全一样:
extension SomeType: SomeProtocol, AnotherProtocol{
// implementation of protocol requirements goes here
}
Adding protocol conformance in this way is described inAdding Protocol Conformance with an Extension.
通过这种方式添加协议一致性的详细描述请参阅利用扩展添加协议一致性。
NOTE
If you define an extension to add new functionality to an existing type, the new functionality will be available on all existing instances of that type, even if they were created before the extension was defined.
如果你通过扩展为一个已有类型添加新功能,那么新功能对该类型的所有已有实例都是可用的,即使它们是在这个扩展定义之前创建的。
Computed Properties (计算型属性)
Extensions can add computed instance properties and computed type properties to existing types. This example adds five computed instance properties to Swift’s built-inDoubletype, to provide basic support for working with distance units:
扩展可以为已有类型添加计算型实例属性和计算型类型属性。下面的例子为 Swift 的内建Double类型添加了五个计算型实例属性,从而提供与距离单位协作的基本支持:
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")
// Prints "One inch is 0.0254 meters"
let threeFeet=3.ft
print("Three feet is\(threeFeet)meters")
// Prints "Three feet is 0.914399970739201 meters"
These computed properties express that aDoublevalue should be considered as a certain unit of length. Although they are implemented as computed properties, the names of these properties can be appended to a floating-point literal value with dot syntax, as a way to use that literal value to perform distance conversions.
这些计算型属性表达的含义是把一个Double值看作是某单位下的长度值。即使它们被实现为计算型属性,但这些属性的名字仍可紧接一个浮点型字面值,从而通过点语法来使用,并以此实现距离转换。
In this example, aDoublevalue of1.0is considered to represent “one meter”. This is why themcomputed property returnsself—the expression1.mis considered to calculate aDoublevalue of1.0.
在上述例子中,Double值1.0用来表示“1米”。这就是为什么计算型属性m返回self,即表达式1.m被认为是计算Double值1.0。
Other units require some conversion to be expressed as a value measured in meters. One kilometer is the same as 1,000 meters, so thekmcomputed property multiplies the value by1_000.00to convert into a number expressed in meters. Similarly, there are 3.28084 feet in a meter, and so theftcomputed property divides the underlyingDoublevalue by3.28084, to convert it from feet to meters.
其它单位则需要一些单位换算。一千米等于 1,000 米,所以计算型属性km要把值乘以1_000.00来实现千米到米的单位换算。类似地,一米有 3.28024 英尺,所以计算型属性ft要把对应的Double值除以3.28024来实现英尺到米的单位换算。
These properties are read-only computed properties, and so they are expressed without thegetkeyword, for brevity. Their return value is of typeDouble, and can be used within mathematical calculations wherever aDoubleis accepted:
这些属性是只读的计算型属性,为了更简洁,省略了get关键字。它们的返回值是Double,而且可以用于所有接受Double值的数学计算中:
let aMarathon=42.km+195.m
print("A marathon is\(aMarathon)meters long")
// Prints "A marathon is 42195.0 meters long"
NOTE
Extensions can add new computed properties, but they cannot add stored properties, or add property observers to existing properties.
扩展可以添加新的计算型属性,但是不可以添加存储型属性,也不可以为已有属性添加属性观察器。
Initializers (构造器)
Extensions can add new initializers to existing types. This enables you to extend other types to accept your own custom types as initializer parameters, or to provide additional initialization options that were not included as part of the type’s original implementation.
扩展可以为已有类型添加新的构造器。这可以让你扩展其它类型,将你自己的定制类型作为其构造器参数,或者提供该类型的原始实现中未提供的额外初始化选项。
Extensions can add new convenience initializers to a class, but they cannot add new designated initializers or deinitializers to a class. Designated initializers and deinitializers must always be provided by the original class implementation.
扩展能为类添加新的便利构造器,但是它们不能为类添加新的指定构造器或析构器。指定构造器和析构器必须总是由原始的类实现来提供。
NOTE
If you use an extension to add an initializer to a value type that provides default values for all of its stored properties and does not define any custom initializers, you can call the default initializer and memberwise initializer for that value type from within your extension’s initializer.
如果你使用扩展为一个值类型添加构造器,同时该值类型的原始实现中未定义任何定制的构造器且所有存储属性提供了默认值,那么我们就可以在扩展中的构造器里调用默认构造器和逐一成员构造器。
This would not be the case if you had written the initializer as part of the value type’s original implementation, as described inInitializer Delegation for Value Types.
正如在值类型的构造器代理中描述的,如果你把定制的构造器写在值类型的原始实现中,上述规则将不再适用。
The example below defines a customRectstructure to represent a geometric rectangle. The example also defines two supporting structures calledSizeandPoint, both of which provide default values of0.0for all of their properties:
下面的例子定义了一个用于描述几何矩形的结构体Rect。这个例子同时定义了两个辅助结构体Size和Point,它们都把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()
}
Because theRectstructure provides default values for all of its properties, it receives a default initializer and a memberwise initializer automatically, as described inDefault Initializers. These initializers can be used to create newRectinstances:
因为结构体Rect未提供定制的构造器,因此它会获得一个逐一成员构造器。又因为它为所有存储型属性提供了默认值,它又会获得一个默认构造器。详情请参阅默认构造器。这些构造器可以用于构造新的Rect实例:
let defaultRect=Rect()
let memberwiseRect=Rect(origin:Point(x:2.0,y:2.0),
size:Size(width:5.0,height:5.0))
You can extend theRectstructure to provide an additional initializer that takes a specific center point and size:
你可以提供一个额外的接受指定中心点和大小的构造器来扩展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)
}
}
This new initializer starts by calculating an appropriate origin point based on the providedcenterpoint andsizevalue. The initializer then calls the structure’s automatic memberwise initializerinit(origin:size:), which stores the new origin and size values in the appropriate properties:
这个新的构造器首先根据提供的center和size的值计算一个合适的原点。然后调用该结构体的逐一成员构造器init(origin:size:),该构造器将新的原点和大小的值保存到了相应的属性中:
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)
NOTE
If you provide a new initializer with an extension, you are still responsible for making sure that each instance is fully initialized once the initializer completes.
如果你使用扩展提供了一个新的构造器,你依旧有责任确保构造过程能够让实例完全初始化。
Methods (方法)
Extensions can add new instance methods and type methods to existing types. The following example adds a new instance method calledrepetitionsto theInttype:
扩展可以为已有类型添加新的实例方法和类型方法。下面的例子为Int类型添加了一个名为repetitions的实例方法:
extensionInt{
func repetitions(task: () ->Void) {
for_in0..<self {
task()
}
}
}
Therepetitions(task:)method takes a single argument of type() -> Void, which indicates a function that has no parameters and does not return a value.
这个repetitions(task:)方法接受一个() -> Void类型的单参数,表示没有参数且没有返回值的函数。
After defining this extension, you can call therepetitions(task:)method on any integer to perform a task that many number of times:
定义该扩展之后,你就可以对任意整数调用repetitions(task:)方法,将闭包中的任务执行整数对应的次数:
3.repetitions{
print("Hello!")
}
// Hello!
// Hello!
// Hello!
Mutating Instance Methods (可变实例方法)
Instance methods added with an extension can also modify (ormutate) the instance itself. Structure and enumeration methods that modifyselfor its properties must mark the instance method asmutating, just like mutating methods from an original implementation.
通过扩展添加的实例方法也可以修改该实例本身。结构体和枚举类型中修改self或其属性的方法必须将该实例方法标注为mutating,正如来自原始实现的可变方法一样。
The example below adds a new mutating method calledsquareto Swift’sInttype, which squares the original value:
下面的例子为 Swift 的Int类型添加了一个名为square的可变方法,用于计算原始值的平方值:
extension Int{
mutating func square() {
self=self*self
}
}
var someInt=3
someInt.square()
// someInt is now 9
Subscripts (下标)
Extensions can add new subscripts to an existing type. This example adds an integer subscript to Swift’s built-inInttype. This subscript[n]returns the decimal digitnplaces in from the right of the number:
扩展可以为已有类型添加新下标。这个例子为 Swift 内建类型Int添加了一个整型下标。该下标[n]返回十进制数字从右向左数的第n个数字:
123456789[0] returns 9
123456789[1] returns 8
…and so on:
……以此类推:
extension Int{
subscript(digitIndex:Int) ->Int{
var decimalBase=1
for_in0..<digitIndex{
decimalBase*=10
}
return(self/decimalBase) %10
}
}
746381295[0]
// returns 5
746381295[1]
// returns 9
746381295[2]
// returns 2
746381295[8]
// returns 7
If theIntvalue does not have enough digits for the requested index, the subscript implementation returns0, as if the number had been padded with zeros to the left:
如果该Int值没有足够的位数,即下标越界,那么上述下标实现会返回0,犹如在数字左边自动补0:
746381295[9]
// returns 0, as if you had requested:
0746381295[9]
Nested Types (嵌套类型)
Extensions can add new nested types to existing classes, structures, and enumerations:
扩展可以为已有的类、结构体和枚举添加新的嵌套类型:
extension Int{
enum Kind{
casenegative,zero,positive
}
var kind:Kind{
switchself{
case0:
return.zero
case let x where x>0:
return.positive
default:
return.negative
}
}
}
This example adds a new nested enumeration toInt. This enumeration, calledKind, expresses the kind of number that a particular integer represents. Specifically, it expresses whether the number is negative, zero, or positive.
该例子为Int添加了嵌套枚举。这个名为Kind的枚举表示特定整数的类型。具体来说,就是表示整数是正数、零或者负数。
This example also adds a new computed instance property toInt, calledkind, which returns the appropriateKindenumeration case for that integer.
这个例子还为Int添加了一个计算型实例属性,即kind,用来根据整数返回适当的Kind枚举成员。
The nested enumeration can now be used with anyIntvalue:
现在,这个嵌套枚举可以和任意Int值一起使用了:
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 + "
This function,printIntegerKinds(_:), takes an input array ofIntvalues and iterates over those values in turn. For each integer in the array, the function considers thekindcomputed property for that integer, and prints an appropriate description.
函数printIntegerKinds(_:)接受一个Int数组,然后对该数组进行迭代。在每次迭代过程中,对当前整数的计算型属性kind的值进行评估,并打印出适当的描述。
NOTE
number.kindis already known to be of typeInt.Kind. Because of this, all of theInt.Kindcase values can be written in shorthand form inside theswitchstatement, such as.negativerather thanInt.Kind.negative.
由于已知number.kind是Int.Kind类型,因此在switch语句中,Int.Kind中的所有成员值都可以使用简写形式,例如使用. Negative而不是Int.Kind.Negative。