以前一個帖子我總結了本身秋招面試經歷,做爲一個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。