Swift
中的扩展有点类似于OC
中的分类(Category
)。
扩展(Extension)可以为枚举、结构体、类、协议添加新功能(方法、计算属性、下标、初始化器(类只能扩展便捷初始化器)、嵌套类型、协议等等)。
扩展不能做的事情:
- 不能覆盖原有的功能
- 不能添加存储属性、不能向已有的属性添加属性观察器
- 不能添加父类
- 不能添加指定初始化器,不能添加反初始化器
- ……
一、结构体
1.1. Double
为Double
添加距离单位:
1 | extension Double { |
1.2. 数组下标
为数组添加下标安全约束,防止数组越界程序崩溃:
1 | extension Array { |
startIndex
:数组开始索引,总是为0endIndex
:数组结束索引,总是大于数组最大下标值,但实际取值不包含该下标值,因此配合..<
操作符使用时,它总是安全的。如果数组为nil
,它的值和startIndex
相等。
1.3. Int
Int
添加扩展功能:
1 | extension Int { |
1.4. 结构体
如果希望自定义初始化器的同时,编译器也能够生成默认初始化器,可以在扩展中编写自定义初始化器。
1 | struct Point { |
注意:扩展限定的指定初始化器是针对类,因为只有类才有指定初始化器的概念。
二、类
类添加扩展需要注意:不能添加指定初始化器。
1 | class Person { |
注意:类遵守协议实现的
required
初始化器,不能写在扩展中。
三、协议
如果一个类型已经实现了协议的所有要求,但是还没有声明它遵守了这个协议,可以通过扩展来让它遵守这个协议。
示例代码:
1 | protocol TestProtocol { |
3.1. 编写一个函数,判断一个整数是否为奇数
示例代码:
1 | func isOdd(_ i: Int) -> Bool { |
如果把变量类型换成UInt
就报错了:
如果把函数修改成泛型函数就无法限制传入的参数是整数类型。怎么解决呢?其实所有的整数都遵守了BinaryInteger
协议,加个泛型约束即可。
1 | func isOdd<T: BinaryInteger>(_ i: T) -> Bool { |
但是这样写成全局函数并不好,最好的方法就是把函数写到只有整数才能调用的地方。
1 | extension BinaryInteger { |
注意:负数一定要加上括号,否则编译器会把后面的小数点一起作为
Double
类型,最终编译报错。
3.2. 注意点
- 扩展可以给协议提供默认实现,也间接实现可选协议的效果
- 扩展可以给协议扩充协议中从未声明过的方法
示例代码一:
1 | protocol TestProtocol { |
由于扩展协议已经实现了协议,所以类遵守协议时不需要再次实现协议内容。
示例代码二:
如果遵守协议的类内部也实现了协议,那么优先执行类中的协议内容。
1 | protocol TestProtocol { |
示例代码三:
1 | protocol TestProtocol { |
为什么test2
的输出是协议中的呢?
由于在协议中没有声明
test2
,所以编译器不能确定将来指向的实例对象是否有test2
的实现。因此把实例
cls
定义为TestProtocol
协议类型后,调用test2
时,编译器会认为test2
在实例里面可能是不存在的,所以直接去协议里优先找。调用类中的
test1
函数的原因是:因为协议中是有声明test1
函数的,而协议默认规定类遵守协议必须实现协议内容,所以会从类中调用test1
四、泛型
在扩展中仍然可以使用原类型中的泛型类型。扩展时也可以对泛型附加约束条件。
示例代码:
1 | class Stack<E> { |