Swift中的Protocol知道這些就夠了

什么是Protocol?


Protocol是Swift中的一種自定義類型,可以使用protocol定義某種約定,而不是某一種類型,一般用于表示某種類型的共性。

Protocol 用法


定義一個protocol
protocol PersonProtocol {
    func getName()
    func getSex()
}
某個class、struct或者enum要遵守這種約定的話,需要實現約定的方法
struct Person: PersonProtocol {
    func getName() {
         print("MelodyZhy")
    }
    func getSex() {
         print("boy")
    }
}
protocol中的約定方法,當方法中有參數時是不能有默認值的
protocol中也可以定義屬性,但必須明確指定該屬性支持的操作:只讀(get)或者是可讀寫(get set)
protocol PersonProtocol {
    // 我們也可以在protocol中定義屬性
    // ??必須明確指定該屬性支持的操作:只讀(get)或者是可讀寫(get set)
    var height: Int { get set }
    func getName()
    func getSex()
    // protocol中的約定方法,當方法中有參數時是不能有默認值的
    // ? Default argument not permitted in a protocol method
    // func getAge(age: Int = 18)
    func getAge(age: Int)
}
雖然height在protocol中是一個computed property,但在遵守該約定的類型中可以簡單的定義成一個stored property
當protocol中定義了一個只讀屬性,其實我們也可以在遵守該約定的類型中完成該屬性的可讀可寫
protocol PersonProtocol {
    var height: Int { get set }
    var weight: Int { get }
    func getName()
    func getSex()
    func getAge(age: Int)
}

struct Person: PersonProtocol {
    var height = 178
    var weight = 120
    func getName() {
         print("MelodyZhy")
    }
    func getSex() {
         print("boy")
    }
    func getAge(age: Int) {
        print("age = \(age)")
    }
}

var person = Person()
person.height   // 178
person.height = 180
person.height  // 180

person.weight // 120
// 可以更改(但在以前版本的Swift中是不能直接這樣更改的
// 需要借助一個內部的stored property
// 然后把這個屬性設計成一個computed property實現 get 和 set 方法)
person.weight = 130 // 130
// 當我們把person從Person轉換成PersonProtocol時 他就是只讀的了
// ?Cannot assign to property: 'weight' is a get-only property
(person as PersonProtocol).weight = 120
如何定義可選的protocol屬性或者方法?
@objc protocol PersonProtocol {
    optional var height: Int { get set }
    optional var weight: Int { get }
    optional func getName()
    optional func getSex()
    optional func getAge(age: Int)
}

class Person: PersonProtocol {
    // 如果想提供可選的約定方法或者屬性那么只能定義@objc的protocol
    // 并且這種約定只能class能遵守
}
protocol可以繼承,當然struct、class、enum都可以同時遵守多個約定
// 例如:
protocol PersonProtocol {
    var height: Int { get set }
    var weight: Int { get }
    func getName()
    func getSex()
    func getAge(age: Int)
}
protocol Engineer: PersonProtocol {
    var good: Bool { get }
}
protocol Animal {
}
struct Person: Engineer, Animal {
    // 省略了該實現約定的方法和屬性
}

這些protocol需要我們掌握


CustomStringConvertible
struct Point {
    var x: Int
    var y: Int
}
let p = Point(x: 10, y: 10)
print(p) // Point(x: 10, y: 10)
// 如果我們想讓打印界面變成 x = 10, y = 10 
// 那么我們就要遵守 CustomStringConvertible 這個 protocol
// 來看下實現
extension Point: CustomStringConvertible {
    // 這個 protocol 只有一個約定定義一個名為description的屬性
    var description: String {
        return "x = \(self.x), y = \(self.y)"
    }
}
print(p) // x = 10, y = 10\n
BooleanType
// 如何更優雅的判斷該點是不是原點(0, 0)
// 來一起看下
extension Point: BooleanType {
    // 遵守BooleanType這個protocol
    // 這個protocol也只需要一個實現一個名為boolValue的屬性
    var boolValue: Bool {
        return self.x == 0 && self.y == 0
    }
}
let p = Point(x: 10, y: 10)
// 這樣我們就能這樣進行判斷了
if p {
    print("是原點(0, 0)")
} else {
    print("不是原點")
}
// 這個protocol可以用到項目的好多地方,開發自己的腦洞去吧!
Equatable
extension Point: Equatable {}

func == (lP: Point, rP: Point) -> Bool {
    let equalX = lP.x == rP.x
    let equalY = lP.y == rP.y
    return equalX && equalY
}
let lP = Point(x: 10, y: 10)
let rP = Point(x: 10, y: 10)
// 不要以為是我們重載了 == 函數 就能自動推導出 !=
// 其實這都是 Equatable 這個 protocol 的功勞
// 當然我們如果要遵守 Equatable 這個 protocol 就必須實現 == 函 數 或者  != 函數
// 如果我們只是重載了 == 函數 是不能用 !=
if lP != rP {
    print("unequal")
} else {
    print("equal")
}
Comparable
// 想遵守 Comparable 這個 protocol 必須遵守 Equatable
// 同時用必須實現 < 函數 或者 > 函數
extension Point: Comparable {}
// 同樣 > 函數會自動推導出來
func < (lP: Point, rP: Point) -> Bool {
    // 隨意定義的規則 不必在意
    let x = lP.x - rP.x
    let y = lP.y - rP.y
    return x < y
}
// 實現了 Comparable 我們就可以把 Point 放到 Array 中,同時支持使用各種排序方法

protocol extension


protocol extension 最關鍵的一點就是能在 protocol extension 方法中獲取 protocol 的屬性,因為Swift編譯器知道任何一個遵守 protocol 的自定義類型,一定會定義這個 protocol 約定的各種屬性,既然這樣我們就可以在 protocol extension 中添加默認的實現了。這也是為什么會有 protocol oriented programming 這個概念,但這時候肯定會有人說我通過面對對象的編程方式也可以實現,但為什么要用遵守 protocol 的方法呢,這個要等到了解 extension 中的 type constraints 后解釋...
先看一個通過 protocol extension 添加默認實現的代碼例子
// 定義一個人屬性的 protocol
protocol PersonProperty {
    var height: Int { get } // cm
    var weight: Double { get } // kg
    // 判斷體重是否合格的函數
    func isStandard() -> Bool
}
extension PersonProperty {
    // 給 protocol 添加默認的實現
    func isStandard() -> Bool {
        return self.weight == Double((height - 100)) * 0.9
    }
    // 給 protocol 添加默認屬性
    var isPerfectHeight: Bool {
        return self.height == 178
    }
}
struct Person: PersonProperty {
    var height: Int
    var weight: Double
    // 如果自定義類型里面創建了遵守的 protocol 中的方法
    // 那么他將覆蓋 protocol 中的方法
//    func isStandard() -> Bool {
//        return true
//    }
}
// 創建遵守 PersonProperty 的自定義類型
let p = Person(height: 178, weight: 61.5)
// 那么 p 這個自定義類型 天生就有判斷這個人身高體重是否合格的方法
p.isStandard() // false
// 同樣天生具有判斷是否是 Perfect Height 的屬性
p.isPerfectHeight // true

protocol extension 中的 type constraints


這相當于給 protocol extension 中的默認實現添加限定條件,寫法如下
// 運動因素的 protocol
protocol SportsFactors {
    // 運動量
    var sportQuantity: Double { get }
}

// 下面這種寫法就用到了 extension 中的 type constraints
// 意思是 只有同時遵守了 SportsFactors 和 PersonProperty 時
// 才使 PersonProperty 獲得擴展 并提供帶有 sportQuantity 屬性的 isStandard 方法
extension PersonProperty where Self: SportsFactors {
    func isStandard() -> Bool {
        // 隨意寫的算法 不要在意
        return self.weight == Double((height - 100)) * 0.9 - self.sportQuantity
    }
}

protocol oriented programming 的優點


1、首先繼承是 class 專有的,所以它不能用來擴展其他類型,但 protocol 是沒有這種局限性的
2、試想一下,上面的代碼你用面對對象的編程方式的話可能你就需要多一個運動量的屬性,同時也要修改 isStandard 函數,一切看起來特別自然,隨著后續需求的更改可能會有更多因素影響是否是合格的體重,那么這時候你就會在不知不覺中將你代碼的耦合度成倍提高,其實對于這個類來說,他完全不需要知道是否是合格體重的計算細節,所以我們完全可以把這些類型無關的細節從類型定義上移出去,用一個 protocol 封裝好這些細節,然后讓其成為這個類型的一種修飾,這就是POP的核心思想。
3、當有多種因素制約是否是合格體重時,我們可以用多個 protocol 來對該類型進行修飾,每一種修飾的相關細節,我們都在對應的 protocol extension 中單獨的封裝起來,這樣就大大降低了代碼的耦合度,同時代碼的可維護性也得到了相應的提高。swift標準庫中大部分都是用這種思想構建的。
最終我們的寫法可能是這樣的
struct Person: PersonProperty, SportsFactors ... {
    var height: Int
    var weight: Double
    var sportQuantity: Double
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,030評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,310評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,951評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,796評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,566評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,055評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,303評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,799評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,683評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,899評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,135評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,520評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,757評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,528評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,844評論 2 372

推薦閱讀更多精彩內容

  • 發現 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,151評論 4 61
  • 山王果刺梨是什么? 山王果刺梨源自于貴州黃龍山脈! 在那里空氣清新!有著很神秘的大山! 刺梨的VC、VP、SOD含...
    山王果刺梨蜜閱讀 4,310評論 0 0
  • 又見花落 周末,清晨,帶孩子出門溜達,路旁,又見被昨夜風吹落的花瓣蜷了一地,當自行車...
    張語純閱讀 404評論 0 0
  • 在現今的社會中,有越來越壯大的一個群體就是江湖上流傳的”剩女”,這一群體的主要特征就是年紀一大把,卻未結婚. 而這...
    耀坤Rosy閱讀 289評論 2 1
  • 奇葩說最新一期有史以來最沉重的一期 就是親人得了絕癥,打算放棄生命,我們該不該勸 當然,這場辯論沒有勝負輸贏 各持...
    大橙橙橙子閱讀 524評論 0 0