之前一个帖子我总结了自己秋招面试经历,作为一个Swift开发者,有一个非常高频的问题就是:你觉得Swift相比于其他语言(或者OC来说)的特点和优势是什么?作为一个见识短浅的小白来说,这个问题实在是不知如何下手啊。这篇文章,也只是从一个小的角度切入,谈一谈Swift中的协议Protocol 和 Protocol Oriented Programming。
面向协议编程 (Protocol Oriented Programming) 是 Apple 在 2015 年 WWDC 上提出的 Swift 的一种编程范式。下面将从Protocol的基本用法开始讲起,最后再分析Protocol在降低代码耦合性方面的优势
《 The Swift Programming Language 》
required
关键字修饰,保证其子类也必须提供该构造器的实现。(除非有final
修饰的类,可以不用required
,因为不会再有子类)let A: [someProtocol]
,遵守某个协议的实例的集合&
关键字,同时遵循多个协议is
、as?
、as!
进行一致性检查class
关键字,限制该协议职能被类继承可选协议:使用optional
修饰属性、函数、协议本身,同时所有option
必须被@objc
修饰,协议本身也必须使用@objc
,只能被Objective-C的类或者@objc
的类使用
where
对协议使用)增加限制条件类(Class) 是面向对象编程之中的重要元素,它代表的是一个共享相同结构和行为的对象的集合
rgin-top:22px;margin-bottom:22px;line-height:inherit;">可能会导致大量保护性拷贝(Defensive Copy),导致效率降低;也有可能发生竞争条件(race condition),出现不可预知的错误;为了避免race condition,需要使用锁(Lock),但是这更会导致代码效率降低,并且有可能导致死锁(Dead Lock)
由于继承时,子类将继承父类全部的属性,所以有可能导致子类过于庞大,逻辑过于复杂。尤其是当父类具有存储属性(stored properties)的时候,子类必须全部继承,并且小心翼翼得初始化,避免损坏父类中的逻辑。如果需要重写(override)父类的方法,则必须要小心思考如何重写以及何时重写。
gb(236, 236, 236);padding-bottom:12px;">Coupling or dependency 耦合性
采用面向协议编程的方式,可以在一定程度上降低代码的耦合性。
耦合性是一种软件度量,是指一程序中,模块及模块之间信息或参数依赖的程度。高耦合性将使得维护成本变高,同时降低代码可复用程度。低耦合性是结构良好程序的特性,低耦合性程序的可读性及可维护性会比较好。
图示是耦合程度由高到低,可粗略分为五个级别:
传统的依赖关系创建在高层次上,而具体的策略设置则应用在低层次的模块上,采用继承的方式实现。依赖反转原则(DIP)是指一种特定的解耦方式,使得高层次的模块不依赖于低层次的模块的实现细节,依赖关系被颠倒(反转),从而使得低层次模块依赖于高层次模块的需求抽象。
DIP 规定:
举一个简单而经典的例子 – 台灯和按钮。
第一幅图为传统的实现方式,依赖关系被创建直接在高层次对象(Button)上,当你需要改变低层次对象(Lamp)时ogramming 面向协议编程
面向协议编程中,Protocol 实际上就是 DIP 中的抽象接口。通过之前的讲解,采用面向协议的方式进行编程,即是对依赖反转原则 DIP 的践行,在一定程度上降低代码的耦合性,避免耦合性过高带来的问题。下面通过一个具体实例简单讲解一下:
Swift// 高层次实现 - EmmettBrown final class EmmettBrown { private let timeMachine: TimeTraveling init(timeMachine: TimeTraveling) { self.timeMachine = timeMachine } func travelInTime(time: TimeInterval) -> String { return timeMachine.travelInTime(time: time) } } 复制代码
采用 Protocol 定义抽象接口 travelInTime,低层次的实现将需要依赖这个接口。
Swift// 抽象接口 - 时光旅行
protocol TimeTraveling {
func travelInTime(time: TimeInterval) -> String
}
复制代码
最后是低层次实现,创建DeLorean类,通过遵循TimeTraveling协议,完成TravelInTime抽象接口的具体实现。
Swift// 低层次实现 - DeLorean
final class DeLorean: TimeTraveling {
func travelInTime(time: TimeInterval) -> String {
return "Used Flux Capacitor and travelled in time by: \\(time)s"
}
}
复制代码
使用的时候只需要创建相关类即可调用其方法。
Swift// 使用方式
let timeMachine = DeLorean()
let mastermind = EmmettBrown(timeMachine: timeMachine)
mastermind.travelInTime(time: -3600 * 8760)
复制代码
Delegation is a design pattern that enables a class or structure to hand off (or delegate) some of its responsibilities to an instance of another type.
委托(Delegate)是一种设计模式,表示将一个对象的部分功能转交给另一个对象。委托模式可以用来响应特定的动作,或者接收外部数据源提供的数据,而无需关心外部数据源的类型。部分情况下,Delegate 比起自上而下的继承具有更松的耦合程度,有效的减少代码的复杂程度。
那么 Deleagte 和 Protocol 之间是什么关系呢?在 Swift 中,Delegate 就是基于 Protocol 实现的,定义 Protocol 来封装那些需要被委托的功能,这样就能确保遵循协议的类型能提供这些功能。
Protocol 是 Swift 的语言特性之一,而 Delegate 是利用了 Protocol 来达到解耦的目的。
swift//定义一个委托
protocol CustomButtonDelegate: AnyObject{
func CustomButtonDidClick()
}
class ACustomButton: UIView {
...
weak var delegate: ButtonDelegate?
func didClick() {
delegate?.CustomButtonDidClick()
}
}
// 遵循委托的类
class ViewController: UIViewController, CustomButtonDelegate {
let view = ACustomButton()
override func viewDidLoad() {
super.viewDidLoad()
...
view.delegate = self
}
func CustomButtonDidClick() {
print("Delegation works!")
}
}
复制代码
如前所述,Delegate 的原理其实很简单。ViewController
会将 ACustomButton
的 delegate
设置为自己,同时自己遵循、实现了 CustomButtonDelegate
协议中的方法。这样在后者调用 didClick
方法的时候会调用 CustomButtonDidClick
方法,从而触发前者中对应的方法,从而打印出 Delegation works!
我们注意到,在声明委托时,我们使用了 weak
关键字。目的是在于避免循环引用。ViewController
拥有 view
,而 view.delegate
又强引用了 ViewController
,如果不将其中一个强引用设置为弱引用,就会造成循环引用的问题。
定义委托时,我们让 protocol 继承自 AnyObject
。这是由于,在 Swift 中,这表示这一个协议只能被应用于 class(而不是 struct 和 enum)。
实际上,如果让 protocol 不继承自任何东西,那也是可以的,这样定义的 Delegate 就可以被应用于 class 以及 struct、enum。由于 Delegate 代表的是遵循了该协议的实例,所以当 Delegate 被应用于 class 时,它就是 Reference type,需要考虑循环引用的问题,因此就必须要用 weak
关键字。
但是这样的问题在于,当 Delegate 被应用于 struct 和 enum 时,它是 Value type,不需要考虑循环引用的问题,也不能被使用 weak
关键字。所以当 Delegate 未限定只能用于 class,Xcode 就会对 weak 关键字报错:‘weak’ may only be applied to class and class-bound protocol types
那么为什么不使用 class 和 NSObjectProtocol,而要使用 AnyObject 呢?NSObjectProtocol 来自 Objective-C,在 pure Swift 的项目中并不推荐使用。class 和 AnyObject 并没有什么区别,在 Xcode 中也能达到相同的功能,但是官方还是推荐使用 AnyObject。
欢迎关注我的公众号,回复关键字“大礼包” ,将会有大礼相送!!! 祝各位面试成功!!!