第二十章 扩展
扩展 (extensions) - 就是给现有的类,结构体,枚举或者协议类型添加新的功能。包括没有读取权限的情况下获取原始源代码的扩展类型的能力(逆向建模),swift中的扩展和oc里面的分类较为相似,但与oc里的分类不同的是swift里面的扩展没有名字。
扩展在swift里有下面这些功能:
- 添加计算型实例属性和计算型类型属性
Add computed instance properties and computed type properties - 定义实例方法和类型方法
Define instance methods and type methods - 提供新的构造器
Provide new initializers - 定义下标
Define subscripts - 定义和使用新的嵌套类型
Define and use new nested types - 确定已有的类型的某个协议
Make an existing type conform to a protocol
在swift语言里面,我们甚至可以对协议进行扩展,提供协议要求的实现,或者添加额外的功能,从而可以让符合协议的类型拥有这些功能,详请见协议扩展章节。
1. Extension Syntax (扩展语法)
用extension关键字声明扩展;
extension SomeType {
// new functionality to add to SomeType goes here
}
通过扩展可以来扩充一个已有的类型,使这个类型采用一个或多个协议并确认这个协议的一致性,我们可以通过扩展来和写类和结构体那样来写一个协议名称。
extension SomeType: SomeProtocol, AnotherProtocol {
// implementation of protocol requirements goes here
}
添加来确认协议的一致性将会在相关章节详细介绍,也可以扩展某个已有的泛型,将会在扩展一个泛型里面有详细介绍,还可以有条件的添加一个功能来扩展该已有的泛型都将会在相关章节中有详细介绍。
2. Computed Property (计算型属性)
扩展可以给现有的类型添加计算型实例属性和计算型类型属性,下面这个例子就是扩展给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 }
}
// inch:英寸 foot:英尺
let oneInch = 25.4.mm // 用dot语法调用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的值看作某个长度单位来计算。即使它们作为一个计算型属性来实现的,这些属性的名字还可以用dot语法来表达一个浮点型的值。从而来实现长度单位的转换。
这些属性是一个只读的计算型属性,所以它们可以不需要get关键字来表达。 Double
类型是这些属性的返回值类型,同样可以用数学计算的方法来实现Double类型值的计算。
// 类型不相同也可以用来计算,因为它们调用的dot语法已经对长度单位不一致的情况作出了转换。
let aMarathon = 42.km + 195.m
print("A marathon is \(aMarathon) meters long")
// 输出:A marathon is 42195.0 meters long
3. Initializers (构造器)
扩展可以在已存在的类型中添加一个新的构造器,使我们用此来扩展其它类型,将你自己的定制类型作为其构造器参数,或者提供该类型的原始实现中未提供的额外初始化选项。
扩展可以使我们给一个类添加一个新的便利构造器,但是不能给这个类添加新的指定构造器和析构器,置顶构造器和析构器只能添加在原有类的实现里面。
下面这个例子是定义了一个自定义的结构体,用来表示这个这个几何长方形。该例子同样定义了两个支持结构体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()
}
因为该结构体Rect提供的是默认值,所以它会自动接收默认构造器和成员逐一构造器。这些构造器用来创建这个新的Rect实例。
let defaultRect = Rect()
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
size: Size(width: 5.0, height: 5.0))
同样可一扩展结构体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)
}
}
这个新的构造器首先根据提供的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)
4. Methods (方法)
扩展可以为已有的类型添加新的实例方法和类型方法,下面这个例子就是给实例方法repetitions
添加一个Int
类型。
extension Int {
func repetitions(task: () -> Void) {
for _ in 0..<self {
task()
}
}
}
这个repetitions(task:)
方法使用的是一个单一的的参数() -> Void
,这个表明了这个函数没有参数并且不会返回值。
在定义完这个扩展之后,我们可以以任何一个Int的值来调用repetitions(task:)方法用来执行多少次的这个任务。
// 三次调用repetitions方法
3.repetitions {
print("Hello!")
}
4.1 Mutating Instance Methods (实例方法的变形)
添加了扩展的实例方法可以修改实例本身,修改self或者属性的结构体和枚举方法都必须要用mutating
关键字来标记这个实例方法。就像在原实现中标记方法那样。
下面这个例子是给swift的Int
类型添加一个可变的方法square
,
extension Int {
// 调用该可变方法
mutating func square() {
// 最终值的计算
self = self * self
}
}
var someInt = 3
someInt.square()
// someInt is now 9
5. Subscripts (下标)
扩展同样也可以给现有的类型添加一个下标,下面这个例子就是添加一个Int类型下标给swift的Int类型,下标[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] // returns 5
746381295[1] // returns 9
746381295[2] // returns 2
746381295[8] // returns 7
6. Nested Types (嵌套类型)
扩展可以为已有的类,结构体,或者枚举添加一个新的嵌套类型。
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枚举
现在可以用温和一个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])
// 输出:+ + - 0 - 0 +
这个函数printIntegerKinds(_:)
采用了一个Int值的输入数组,然后进行迭代,每一个在数组中的Int值,函数为这个Kind的计算型属性采用的是整数值。并输出合适的表述。