Swift学习之路-Extension

请在阅读本文章时,顺手将文中的示例代码在playground中敲一遍,这样能加深理解!!!
阅读该文章大约需要:15分钟
读完之后你能获得:
1、Extension是什么
2、它能做什么

本文全部内容基于Swift版本:3.0.1

Extension的基本语法

1
2
3
extension SomeType {
// new functionality to add to SomeType goes here
}

Tip:扩展可以为一个类型添加新的功能,但是不能重写已有的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
struct Student {
var name = ""
var age = 1
func print() {
}
}
extension Student {
//改行会报错`invalid redeclaration print()`重复声明print(),重写变量也是不行的。
func print() {
}
}

Swift中的Extension可以做什么

我们想知道Extension在Swift中能做些什么,最直接的方法就是查看Swift的官方文档了。下面是文档中指出Extension能做的六个方面。

  • 添加计算型实例属性和计算型类型属性
  • 定义实例方法和类型方法
  • 提供新的构造器
  • 定义下标
  • 定义和使用新的嵌套类型
  • 使已存在的类型遵守某个协议

看完上面Extension能做的六个方面,我们来逐条解释说明一下:

添加计算型实例属性和计算型类型属性

首先我们要了解什么是计算型属性,计算型属性(computed property)不直接存储值,而是提供一个getter和一个可选的setter,来间接获取和设置其他属性或变量的值。关于更多的计算型属性的内容请自行查看官方文档,在此不再赘述。那么我们什么场景可以用到这个功能呢?举一个最常见的例子,当你想访问某个view的width的时候,通常情况下你会这么写:

1
view.frame.size.width

但是这样写很长很不方便,作为一个懒惰的程序员,这时候你就要想我能不能缩短访问该属性的代码。不要犹豫了骚年,这时候你只需要写一个UIView的Extension就可以达到你的目的。

1
2
3
4
5
6
extension UIView {
var x: CGFloat { return self.frame.origin.x }
var y: CGFloat { return self.frame.origin.y }
var width: CGFloat { return self.frame.size.width }
var height: CGFloat { return self.frame.size.height }
}

这样你就可以通过来访问该属性。怎么样,有没有感受到Extension的便利之处。

1
view.width

定义实例方法和类型方法

在你辛辛苦苦写完一个Student类后,万恶的产品过来告诉你需求改了,这时虽然你心中有一万只草泥马在奔腾,但是为了心中那份神圣的程序员的责任感(当然还有糊口的工资),你还是要修改代码。如果你想在不改变原始类的基础上添加功能,那你可以给Student类添加Extension来解决问题。

Tip:这里值得注意的一点是在Swift中,Extension可以给类和类型添加,比如你也可以给一个struct添加Extension,而在Objective-C中,你只能给类添加Extension。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Student {
var name = ""
var age = 1
}
extension Student {
func printCurrentStudentName() {
print(self.name)
}
}
var jack = Student()
jack.name = "jack"
jack.printCurrentStudentName()

提供新的构造器(Initializers)

最常见的Rect通常由originsize来构造初始化,但是如果在你写完Rect的定义后,你偏偏想要通过center和size来确定Rect(作为一个程序员就要有一种作死的精神),那你就要用Extension来给Rect提供一个新的构造器。关于更多关于构造器的信息,请参考官方文档

本例子来源官方文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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()
}
let defaultRect = Rect()
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
size: Size(width: 5.0, height: 5.0))

通过Extension来给Rect添加一个新的构造器。

1
2
3
4
5
6
7
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)
}
}

这样你就可以通过新的构造器来初始化Rect。

1
2
3
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)

定义下标

通过Swift中的Extension,你可以给已知类型添加下标。例如下面的例子就是给Int类型添加一个下标,该下标表示十进制数从右向左的第n个数字。

本例子来源官方文档

1
2
3
4
5
6
7
8
9
10
11
12
13
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

定义和使用新的嵌套类型(nest type)

Extensions可以给已知的类、结构体、枚举添加嵌套类型。下面的例子是给Int类型添加一个判断正负数的Extension,该Extension嵌套一个枚举。

本例子来源官方文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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
}
}
}
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 + "

使已存在的类型遵守某个协议

编写使该类型遵守某个协议的Extension的语法如下:

1
2
3
extension SomeType: SomeProtocol, AnotherProtocol {
// implementation of protocol requirements goes here
}

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
protocol StudentProtocol {
var address: String { get }
}
struct Student {
var name = ""
var age = 1
}
extension Student: StudentProtocol {
var address: String {
return "address"
}
}
var jack = Student()
jack.address
//输出 address

若添加Extension的类型已经实现协议中的内容,你可以写一个空的Extension来遵守协议:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
protocol StudentProtocol {
var address: String { get }
}
struct Student {
var address: String {
return "address"
}
var name = ""
var age = 1
}
extension Student: StudentProtocol {}
var jack = Student()
jack.address
//输出 address

总结

  • Extension可以为一个已有的类、结构体、枚举类型或者协议类型添加新功能。
  • 可以在没有权限获取原始源代码的情况下扩展类型的内容
  • Extendion和Objective-C中的Category类似。(OC中的Category有名字,Swift中的扩展没有名字)

下篇预告:Swift-Protocol

若本文有何错误或者不当之处,还望不吝赐教。谢谢!

Swift-Extension的官方文档