以前一个帖子我总结了本身秋招面试经历,做为一个Swift开发者,有一个很是高频的问题就是:你以为Swift相比于其余语言(或者OC来讲)的特色和优点是什么?做为一个见识短浅的小白来讲,这个问题实在是不知如何下手啊。这篇文章,也只是从一个小的角度切入,谈一谈Swift中的协议Protocol 和 Protocol Oriented Programming。html
面向协议编程 (Protocol Oriented Programming) 是 Apple 在 2015 年 WWDC 上提出的 Swift 的一种编程范式。下面将从Protocol的基本用法开始讲起,最后再分析Protocol在下降代码耦合性方面的优点git
《 The Swift Programming Language 》github
required
关键字修饰,保证其子类也必须提供该构造器的实现。(除非有final
修饰的类,能够不用required
,由于不会再有子类)let A: [someProtocol]
,遵照某个协议的实例的集合&
关键字,同时遵循多个协议is
、as?
、as!
进行一致性检查class
关键字,限制该协议职能被类继承可选协议:使用optional
修饰属性、函数、协议自己,同时全部option
必须被@objc
修饰,协议自己也必须使用@objc
,只能被Objective-C的类或者@objc
的类使用面试
where
对协议使用)增长限制条件类(Class) 是面向对象编程之中的重要元素,它表明的是一个共享相同结构和行为的对象的集合编程
可能会致使大量保护性拷贝(Defensive Copy),致使效率下降;也有可能发生竞争条件(race condition),出现不可预知的错误;为了不race condition,须要使用锁(Lock),可是这更会致使代码效率下降,而且有可能致使死锁(Dead Lock)swift
因为继承时,子类将继承父类所有的属性,因此有可能致使子类过于庞大,逻辑过于复杂。尤为是当父类具备存储属性(stored properties)的时候,子类必须所有继承,而且当心翼翼得初始化,避免损坏父类中的逻辑。若是须要重写(override)父类的方法,则必需要当心思考如何重写以及什么时候重写。设计模式
上图中,两个类(Label、Number)拥有相同的父类(Ordered),可是在 Number 中调用 Order 类必需要使用强制解析(as!)来判断 Other 的属性,这样作既不优雅,也很是容易出Bug(若是 Other 碰巧为Label类)数据结构
采用面向协议编程的方式,能够在必定程度上下降代码的耦合性。app
耦合性是一种软件度量,是指一程序中,模块及模块之间信息或参数依赖的程度。高耦合性将使得维护成本变高,同时下降代码可复用程度。低耦合性是结构良好程序的特性,低耦合性程序的可读性及可维护性会比较好。ide
图示是耦合程度由高到低,可粗略分为五个级别:
传统的依赖关系建立在高层次上,而具体的策略设置则应用在低层次的模块上,采用继承的方式实现。依赖反转原则(DIP)是指一种特定的解耦方式,使得高层次的模块不依赖于低层次的模块的实现细节,依赖关系被颠倒(反转),从而使得低层次模块依赖于高层次模块的需求抽象。
DIP 规定:
举一个简单而经典的例子 -- 台灯和按钮。
第一幅图为传统的实现方式,依赖关系被建立直接在高层次对象(Button)上,当你须要改变低层次对象(Lamp)时,你必需要同时更改其父类(Button),若是此时有多个低层次的对象继承自父类(Button),那么更改其父类就变得十分困难。而第二幅图是符合DIP原则的方式,高层对象(Button)把需求抽象为一个抽象接口(ButtonServer),而具体实现(Lamp)依赖于这个抽象接口。同时,当须要实现多个底层对象时,只须要在具体实现时进行不一样的实现便可。
面向协议编程中,Protocol 实际上就是 DIP 中的抽象接口。经过以前的讲解,采用面向协议的方式进行编程,便是对依赖反转原则 DIP 的践行,在必定程度上下降代码的耦合性,避免耦合性太高带来的问题。下面经过一个具体实例简单讲解一下:
首先是高层次结构的实现,建立EmmettBrown的类,而后声明了一个需求(travelInTime方法)。
// 高层次实现 - 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,低层次的实现将须要依赖这个接口。
// 抽象接口 - 时光旅行
protocol TimeTraveling {
func travelInTime(time: TimeInterval) -> String
}
复制代码
最后是低层次实现,建立DeLorean类,经过遵循TimeTraveling协议,完成TravelInTime抽象接口的具体实现。
// 低层次实现 - DeLorean
final class DeLorean: TimeTraveling {
func travelInTime(time: TimeInterval) -> String {
return "Used Flux Capacitor and travelled in time by: \(time)s"
}
}
复制代码
使用的时候只须要建立相关类便可调用其方法。
// 使用方式
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 来达到解耦的目的。
//定义一个委托
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。