1. 扩展(Extension)
Swift中的扩展,有点类似于OC中的分类(Category)
扩展可以为枚举、结构体、类、协议添加新功能
可以添加计算属性、方法、下标、嵌套类型、协议、便捷初始化器等等
扩展不能办到的事情:
不能覆盖原有的功能
不能添加存储属性,不能向已有的属性添加属性观察器
不能添加父类
不能添加指定初始化器,不能添加反初始化器
......
为什么扩展不能添加存储属性?因为存储属性会影响内存结构,扩展是不允许影响内存结构的,所以不能添加。
同理,不能添加父类,也是因为继承也有可能改变内存结构。
指定初始化器、反初始化器这两个比较重要的初始化器肯定不可以通过扩展添加。
2. 扩展计算属性、方法、下标、嵌套类型
扩展Double计算属性:
extension Double {
var km: Double { self * 1_000.0 }
var m: Double { self }
var dm: Double { self / 10.0 }
var cm: Double { self / 100.0 }
var mm: Double { self / 1_000.0 }
}
var d = 100.0
print(d.m) //100.0
print(d.km) //100000.0
print(d.dm) //10.0
print(100.0.m) //100.0
扩展Array下标:
//给数组添加一个下标,防止下标越界报错
var arr: Array<Int> = [10, 20, 30]
extension Array {
subscript(nullable idx: Int) -> Element? {
if (startIndex..<endIndex).contains(idx) {
return self[idx]
}
return nil
}
}
print(arr[nullable: 1] ?? 0) //20
print(arr[nullable: 10] ?? 0) //0
扩展Int方法、嵌套枚举、下标:
extension Int {
func repetitions(task: () -> Void) { //重复任务
for _ in 0..<self { task() }
}
mutating func square() -> Int { //求平方
self = self * self
return self
}
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
}
}
subscript(digitIndex: Int) -> Int { //根据下标获取个、十、百位
var decimalBase = 1
for _ in 0..<digitIndex { decimalBase *= 10 }
return (self / decimalBase) % 10
}
}
3.repetitions {
print("重复打印三次")
}
var age = 10
print(age.square()) //100 求平方
//Int.Kind.negative //定义嵌套类型
var age1 = -10
print(age1.kind) //negative
var age3 = 789
print(age3[2]) //7 0获取个位,1获取十位,2获取百位
3. 扩展协议、便捷初始化器
给类扩展协议、便捷初始化器
class Person {
var age: Int
var name: String
init(age: Int, name: String) {
self.age = age
self.name = name
}
}
extension Person : Equatable { //扩展协议
static func == (left: Person, right: Person) -> Bool {
left.age == right.age && left.name == right.name
}
convenience init() { //扩展便携初始化器
self.init(age: 0, name: "")
}
}
给结构体扩展初始化器
struct Point {
var x: Int = 0
var y: Int = 0
}
extension Point {
init(_ point: Point) { //给结构体扩展初始化器
self.init(x: point.x, y: point.y)
}
}
//以前我们说过,如果我们给结构体自定义初始化器那么编译器就不会给你生成默认初始化器了
//但是在扩展里面扩展初始化器编译器仍然会给你生成默认初始化器
var p1 = Point()
var p2 = Point(x: 10)
var p3 = Point(y: 20)
var p4 = Point(x: 10, y: 20)
var p5 = Point(p4)
注意1:指定初始化器、便捷初始化器是类里面的概念,在结构体里面没这个概念,就是直接扩展一个初始化器
注意2:如果希望自定义初始化器的同时,编译器也能够生成默认初始化器,可以在扩展中编写自定义初始化器
注意3:required初始化器不能写在扩展中,只能写在本类中
4. 扩展中遵守协议、给协议扩展方法
如果一个类型已经实现了协议的所有要求,但是还没有声明它遵守了这个协议,可以通过扩展来让它遵守这个协议
protocol TestProtocol {
func test()
}
class TestClass {
func test() {
print("test")
}
}
extension TestClass : TestProtocol {}
补充:编写一个函数,判断一个整数是否为奇数?
方法一:
//整数的共同点就是都遵守BinaryInteger协议,所以参数传入遵守BinaryInteger协议的东西
func isOdd<T: BinaryInteger>(_ i: T) -> Bool { //判断是否为奇数
i % 2 != 0
}
方法二:
//上面方法的缺点就是上面方法暴露在外面了,其实它只是整数相关的方法
//所以我们可以写在BinaryInteger协议扩展里面,在扩展里面实现isOdd方法,这样遵守BinaryInteger协议的东西都可以使用这个方法了
extension BinaryInteger {
func isOdd() -> Bool { self % 2 != 0 }
}
print(10.isOdd()) //false
print(-3.isOdd()) //true
5. 利用扩展实现可选协议
扩展可以给协议提供默认实现,也间接实现可选协议的效果
扩展可以给协议扩充协议中从未声明过的方法
protocol TestProtocol {
func test1()
}
extension TestProtocol {
func test1() { //扩展可以给协议提供默认实现,也间接实现可选协议的效果
print("TestProtocol test1")
}
func test2() { //扩展可以给协议扩充协议中从未声明过的方法
print("TestProtocol test2")
}
}
我们知道,协议中的方法是必须都要实现的,但是如果在扩展中给协议提供了默认实现,那么一个类遵守协议的时候可以不实现这个方法。
如下,TestClass遵守了TestProtocol协议,但是并未实现test1方法,但是没报错,而且可以正常调用。
class TestClass : TestProtocol {}
var cls = TestClass()
cls.test1() // TestProtocol test1
cls.test2() // TestProtocol test2
如果TestClass也实现了test1、test2方法呢?
class TestClass : TestProtocol {
func test1() { print("TestClass test1") }
func test2() { print("TestClass test2") }
}
var cls = TestClass()
cls.test1() // TestClass test1
cls.test2() // TestClass test2
var cls2: TestProtocol = TestClass() //看下面解释👇
cls2.test1() // TestClass test1
cls2.test2() // TestProtocol test2
- 如果cls是TestClass类型的,自然调用的都是TestClass的方法,这个没什么可说的。
- 如果将cls2定义成TestProtocol类型的,test1在协议里面有定义,所以实例里面必须有实现,就直接去实例里面找,所以调用的是TestClass的test1方法。
由于test2在协议里面没定义,所以test2方法在TestClass里面可能不存在,这时候编译器就当作它不存在,就不去实例里面找了,就直接去协议里面找,所以调用的是TestProtocol的test2方法。
6. 关于泛型
扩展里面依然可以使用原类型中的泛型类型
class Stack<E> {
var elements = [E]()
func push(_ element: E) {
elements.append(element)
}
func pop() -> E { elements.removeLast() }
func size() -> Int { elements.count }
}
//扩展里面依然可以使用原类型中的泛型类型
extension Stack {
func top() -> E { elements.last! }
}
符合条件才扩展:如果E遵守Equatable协议,那么下面的扩展才生效,否则不生效
extension Stack : Equatable where E : Equatable {
static func == (left: Stack, right: Stack) -> Bool {
left.elements == right.elements
}
}
网友评论