Swift中可以对类,结构体,协议,枚举进行拓展添加新的功能
考虑到本篇博文中的篇幅,关于枚举的拓展将在下一篇介绍ps:基于Swift 5
Swift 中的扩展可以:

  • 一、添加计算型属性和计算型静态属性
  • 二、定义实例方法和类型方法
  • 三、提供新的构造器
  • 四、定义下标
  • 五、定义和使用新的嵌套类型
  • 六、使一个已有类型符合某个协议

    一、添加计算型属性、计算型静态属性、“存储属性”

    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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    class Animal:NSObject {
    var age:Int?
    }

    private var animalName:String = ""
    extension Animal {
    //1添加静态计算属性
    static var c:Int{
    get {
    print("get c")
    return 3
    }
    set{
    print("set c:\(newValue)")
    }
    }
    //2添加计算属性
    var isAdult:Bool{
    get{
    return self.age ?? 0 > 2
    }
    set{
    if newValue {
    self.age = 2
    }else{
    self.age = 0
    }
    }
    }
    //3使用AssociatedObject添加存储属性
    var name:String {
    get{
    return objc_getAssociatedObject(self, &animalName) as! String
    }
    set{
    objc_setAssociatedObject(self, &animalName, newValue, .OBJC_ASSOCIATION_ASSIGN)
    }
    }

    }
    调用示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    let animal = Animal()
    animal.age = 3
    print("\(animal.age)")//Optional(3)
    animal.name = "AA"
    animal.isAdult = true
    print("-----")
    print("\(animal.age)")//Optional(2)
    print("\(animal.name)")//AA
    Animal.c = 6
    print(Animal.c)
    调用结果
    1,这里需要解释一下官方文档中虽然介绍extension不能添加存储属性,但是依旧可以利用AssociatedObject来达到添加存储属性的效果
    2,在类和结构体中添加计算属性,“存储属性”可以按照以上方式,但是在protocol中又会是何种情况呢?我们知道protocol中可以定义属性和方法,而遵循该protocol的类或结构体就必须实现其中的方法,添加其中的属性(Optional除外)。如果在定义protocol时并没有申明子类必须添加某个属性,而你通过拓展添加了一个存储属性,那你之前遵循过此协议的子类岂不是都不能编译通过了。因此在拓展协议时只能添加Optional属性。
    在协议中添加optional属性
    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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    protocol P{
    var name:String{ get set}

    }
    extension P{
    //添加静态计算属性
    static var c:Int{
    get {
    print("get c")
    return 3
    }
    set{
    print("set c:\(newValue)")
    }
    }
    //添加计算属性
    var temp:Bool{
    get{
    return true
    }

    set{
    print("set temp:\(newValue)")
    }
    }
    //使用AssociatedObject添加存储属性
    var words:String? {
    get{
    return objc_getAssociatedObject(self, &animalName) as? String
    }
    set{
    objc_setAssociatedObject(self, &animalName, newValue, .OBJC_ASSOCIATION_ASSIGN)
    }
    }
    }

    class B : P{
    var name: String = ""

    }
    调用结果

二、定义实例方法和类型方法

1
2
3
4
5
6
7
8
9
extension String {
func fetchDate(_ format :String = "YYYY-MM-dd HH:mm:ss") ->Date?{
let dateFormater = DateFormatter.init()
dateFormater.dateFormat = format

let date = dateFormater.date(from: self)
return date
}
}

1,在结构体和类中添加拓展方法应该是日常开发中最为常见的
2,在拓展类时添加实例方法既可以用class修饰也可以使用static修饰,而在结构体和协议中只能使用static修饰,因为Class methods are only allowed within classes

三、提供新的构造器

1,给结构体添加新的构造器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct H{
var num1:Int?
var num2:Int?
}

extension H{
init(num1:Int,num2:Int) {
self.num1 = num1
self.num2 = num2
}

init(num1:Int){
self.num1 = num1
}
}
let h = H(num1: 2, num2: 3)
print(h.num1)//Optional(2)
print(h.num2)//Optional(3)

结构体构造函数
从上图的代码提示中可以看到struct H在初始化时有系统默认生成的一个无参构造函数和一个带有所有属性的构造函数,以及我们在extension中添加的两个构造函数

2,给类添加新的构造器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
extension UIColor{
/// hexColor
convenience init(hex: String , alpha: CGFloat = 1) {
let scanner = Scanner(string: hex)
scanner.scanLocation = 0

var rgbValue: UInt64 = 0

scanner.scanHexInt64(&rgbValue)

let r = (rgbValue & 0xff0000) >> 16
let g = (rgbValue & 0xff00) >> 8
let b = rgbValue & 0xff

self.init(
red: CGFloat(r) / 0xff,
green: CGFloat(g) / 0xff,
blue: CGFloat(b) / 0xff, alpha: alpha
)
}

}

struct不同的是在拓展类时只能添加便利构造器

四、定义下标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
extension String
{
/// 返回索引为index的字符,未找到返回空
subscript(index: Int) -> String {
let range = 0...self.count - 1
if range.contains(index){
let s = self.index(startIndex, offsetBy: index)
let e = self.index(startIndex, offsetBy: index + 1)
return String(self[s..<e])
}
return ""
}
}

let str1 = "12345"
print(str1[-3])// ""
print(str1[3])// "4"

类同理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class People{

}

extension People{
subscript(index: Int) -> String {

return "AA"
}
subscript(a:String,b:Int) -> Int{
return 3
}
}
let p = People()
print(p[2])//AA
print(p["SS",0])//3

ps:对于类拓展下标的功能写完我真的是给Swift跪了,可以自定义参数,自定义返回值。上面拓展String的下标访问还可以理解,对于给类添加下标访问方法目前我还没有发现有什么妙用,如果有什么好案例还望不吝赐教。

五、定义嵌套类型

1
2
3
4
5
6
7
8
9
10
11
12
class People{

}

extension People{
enum gender{
case man
case woman
}
}
// 使用
People.gender.man

六、使一个已有类型符合某个协议

1
2
3
4
5
6
7
8
9
10
11
extension MAOrderListSecondViewController :UITableViewDelegate,UITableViewDataSource{

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
//....
}

在开发中我经常将某个类需要遵循的协议使用extension分割开来以提高代码的可读性

七、拓展协议添加where从句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
protocol MAViewProtocol {

}

extension MAViewProtocol where Self:UIView{
func getString() -> String{
return "String"
}
}

class MATestColor:UIColor,MAViewProtocol{

}

class MATestView:UIView,MAViewProtocol {

}

let view = MATestView()
view.getString()
let color = MATestColor()
color.getString()

上面的代码段中color.getString()是无法通过编译的。首先我在MAViewProtocol的拓展中添加了getString()方法虽然MATestColorMATestView都遵循了MAViewProtocol,但是我在拓展时添加了where从句where Self:UIView。意思为只有是UIView的子类才能使用该拓展中的方法,而MATestColor继承自UIColor因此无法调用getString()方法

八、通过拓展协议实现命名空间

从事iOS开发的同学应该不会对Kingfisher中的kf,RxSwiftrx感到陌生

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public struct Reactive<Base> {
/// Base object to extend.
public let base: Base

/// Creates extensions with base object.
///
/// - parameter base: Base object.
public init(_ base: Base) {
self.base = base
}
}

/// A type that has reactive extensions.
public protocol ReactiveCompatible {
/// Extended type
associatedtype ReactiveBase

@available(*, deprecated, renamed: "ReactiveBase")
typealias CompatibleType = ReactiveBase

/// Reactive extensions.
static var rx: Reactive<ReactiveBase>.Type { get set }

/// Reactive extensions.
var rx: Reactive<ReactiveBase> { get set }
}

extension ReactiveCompatible {
/// Reactive extensions.
public static var rx: Reactive<Self>.Type {
get {
return Reactive<Self>.self
}
// swiftlint:disable:next unused_setter_value
set {
// this enables using Reactive to "mutate" base type
}
}

/// Reactive extensions.
public var rx: Reactive<Self> {
get {
return Reactive(self)
}
// swiftlint:disable:next unused_setter_value
set {
// this enables using Reactive to "mutate" base object
}
}
}

这段代码是从RxSwift框架中截取出来,目前对于我来说只是知其然不知其所以然,后续再慢慢深究吧。

参考文献
https://docs.swift.org/swift-book/LanguageGuide/Extensions.html#//apple_ref/doc/uid/TP40014097-CH24-ID152
https://www.runoob.com/swift/swift-extensions.html

Swift
仿App Store转场动画 Swift语言实现Moya网络请求封装