mirror of
https://github.com/NohamR/knowledge-kit.git
synced 2026-05-25 04:17:17 +00:00
docs: clang 插件开发
This commit is contained in:
159
Chapter1 - iOS/1.126.md
Normal file
159
Chapter1 - iOS/1.126.md
Normal file
@@ -0,0 +1,159 @@
|
||||
# Swift 面向协议编程
|
||||
|
||||
## 概念
|
||||
|
||||
面向协议编程(Protocol Oriented Programming,简称 POP)
|
||||
|
||||
- 是 Swift 的一种编程范式, Apple 于2015年 WWDC 提出
|
||||
- 在 Swift 的标准库中,能见到大量 POP 的影子
|
||||
|
||||
同时,Swift 也是一门面向对象的编程语言(Object Oriented Programming,简称OOP)
|
||||
在 Swift 开发中,OOP 和 POP 是相辅相成的,任何一方并不能取代另一方,POP 能弥补 OOP 一些设计上的不足
|
||||
|
||||
|
||||
|
||||
## 优势
|
||||
|
||||
OOP 三大特性:继承、封装、多态
|
||||
|
||||
继承的经典使用场合:当多个类(比如 A、B、C 类)具有很多共性时,可以将这些共性抽取到一个父类中(比如 D 类),最后A、B、C类继承 D 类。
|
||||
|
||||
但一些情况下,继承并不能解决问题
|
||||
|
||||
比如 AVC 继承自 UIViewController 有 run 方法,BVC 继承自 UITableViewController,有 run 方法。AVC、BVC 有重复代码,如何消除?继承吗?可能会存在菱形继承问题。
|
||||
|
||||
菱形继承,也称为钻石继承或多头继承,发生在当一个类从两个或多个具有共同父类的类继承时。这种结构形成了一个菱形的继承图,因此得名。菱形继承可能导致一些问题,主要是二义性和数据冗余。
|
||||
|
||||
1. **二义性**:在菱形继承中,子类可能会从多个路径继承同一个基类的方法或属性。这会导致在子类中调用该方法或属性时存在不确定性,编译器或解释器不知道应该使用哪个版本的方法或属性。这种不确定性可能导致难以调试的错误和不可预测的行为。
|
||||
2. **数据冗余**:菱形继承也可能导致数据冗余。如果子类从多个父类继承了相同的数据成员,那么这些数据成员在内存中会有多个拷贝。这不仅浪费了存储空间,而且可能导致数据不一致的问题,因为对其中一个拷贝的修改不会影响到其他拷贝。
|
||||
|
||||
为了解决这些问题,一些编程语言提供了虚继承(virtual inheritance)的机制。虚继承允许子类只继承共享基类的一个拷贝,从而避免了二义性和数据冗余的问题。在虚继承中,共享基类被视为虚基类,子类只通过其中一个父类继承该基类的实现。
|
||||
|
||||
然而,虚继承并不是没有代价的。它可能会引入一些性能开销,因为编译器需要处理额外的间接引用和内存布局问题。此外,虚继承也可能使代码更加复杂和难以理解,特别是对于不熟悉该概念的开发者来说。
|
||||
|
||||
因此,在使用多继承时,需要谨慎考虑其潜在的问题和代价。在可能的情况下,尽量避免菱形继承结构,并通过其他方式(如接口、组合或委托)来实现所需的功能。如果必须使用菱形继承,那么应该仔细规划类的设计和继承关系,并充分利用虚继承等机制来减少潜在的问题。
|
||||
|
||||
采用 POP 实现。
|
||||
|
||||
```swift
|
||||
protocol Runable {
|
||||
func run()
|
||||
}
|
||||
extension Runable {
|
||||
func run() {
|
||||
print("running")
|
||||
}
|
||||
}
|
||||
|
||||
class AVC: UIViewController, Runable {
|
||||
// ...
|
||||
}
|
||||
class BVC: UITableViewController, Runable {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 思想转换
|
||||
|
||||
- 优先考虑创建协议,而不是基类
|
||||
- 优先考虑值类型(struct,enum),而不是引用类型(class)
|
||||
- 巧用协议的拓展功能 (extension Runable { ... })
|
||||
- 不要为了面向协议而使用协议
|
||||
|
||||
|
||||
|
||||
## 应用
|
||||
|
||||
统计字符串中数字个数
|
||||
|
||||
```swift
|
||||
// 方法1
|
||||
var testString: String = "ab1783893cs"
|
||||
extension String {
|
||||
var countOfNumber: Int {
|
||||
var count: Int = 0
|
||||
for c in self where ("0"..."9").contains(c) {
|
||||
count += 1
|
||||
}
|
||||
return count
|
||||
}
|
||||
}
|
||||
print(testString.countOfNumber) // 7
|
||||
|
||||
// 方法2.优雅的 Swift 风格
|
||||
struct Counter {
|
||||
var originalString: String
|
||||
init(originalString: String) {
|
||||
self.originalString = originalString
|
||||
}
|
||||
var countOfNumber: Int {
|
||||
var count: Int = 0
|
||||
for c in originalString where ("0"..."9").contains(c) {
|
||||
count += 1
|
||||
}
|
||||
return count
|
||||
}
|
||||
}
|
||||
extension String {
|
||||
var counter: Counter {
|
||||
Counter(originalString: self)
|
||||
}
|
||||
}
|
||||
print(testString.counter.countOfNumber) // 7
|
||||
```
|
||||
|
||||
上述2个方法虽然实现了统计功能,但是不够优雅,没有那么的 Swift 化。改写如下
|
||||
|
||||
```swift
|
||||
struct MY<Base> {
|
||||
let base: Base
|
||||
init(base: Base) {
|
||||
self.base = base
|
||||
}
|
||||
}
|
||||
|
||||
protocol MyCompitable {}
|
||||
extension MyCompitable {
|
||||
var my: MY<Self> {
|
||||
set{}
|
||||
get{ MY(base: self)}
|
||||
}
|
||||
static var my: MY<Self>.Type {
|
||||
set{}
|
||||
get{ MY<Self>.self }
|
||||
}
|
||||
}
|
||||
|
||||
extension String: MyCompitable { }
|
||||
extension MY where Base == String {
|
||||
func countOfNumber() -> Int {
|
||||
var count: Int = 0
|
||||
for c in base where ("0"..."9").contains(c) {
|
||||
count += 1
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
static func test() {
|
||||
print("I am a static method")
|
||||
}
|
||||
|
||||
mutating func modify() {
|
||||
print("I am a mutating method")
|
||||
}
|
||||
}
|
||||
print(testString.my.countOfNumber())
|
||||
String.my.test()
|
||||
testString.my.modify()
|
||||
// console
|
||||
7
|
||||
I am a static method
|
||||
I am a mutating method
|
||||
```
|
||||
|
||||
要拓展系统提供的类型,可以按照上述模版进行修改。
|
||||
|
||||
- 加前缀 `my` 的目的是防止重复,系统实现是黑盒,如果自己直接提供类似 `testString.countOfNumber` 怕后续系统也提供 `countOfNumber` 方法。所以加前缀 `testString.my.countOfNumber`
|
||||
-
|
||||
Reference in New Issue
Block a user