[swift 進階]讀書筆記-第十章:協議 C10P1 面向協議編程 Overload Resolution for Free Functions

第十章:協議 Protocol Protocol-Oriented Programming

前言:

swift 中的協議仍是很酷的😎node

1.能夠當作代理來使用。編程

2.可讓結構體枚舉來知足協議。swift

3.還能夠經過協議的extension爲協議添加新方法。app

4.協議容許咱們動態派發svg

5.OC中共享代碼一般使用的繼承,swift中能夠經過使用的是協議來共享代碼spa

6.你能夠爲你的類添加協議去達到功能點整合代理

10.1 面向協議編程 Overload Resolution for Free Functions

這一小節舉了個例子來介紹面相協議編程的幾個使用場景code

圖形渲染的Demo

將 Core Graphics 的 CGContext 渲染到屏幕上,或者建立一個 SVG 格式的圖形文件。咱們能夠從定義繪圖 API 的最 小功能集的協議開始進行實現繼承

1.先寫協議方法ip

protocol Drawing {
    mutating func addEllipse(rect: CGRect, fill: UIColor) mutating 
    func addRectangle(rect: CGRect, fill: UIColor)
}
複製代碼

2.爲CGContext添加擴展來知足協議

extension CGContext: Drawing {
    func addEllipse(rect: CGRect, fill: UIColor) {
        setFillColor(fill.cgColor)
        fillEllipse(in: rect) }
    func addRectangle(rect: CGRect, fill fillColor: UIColor) { 
        setFillColor(fillColor.cgColor)
        fill(rect)
    } 
}
複製代碼
  1. 對自定義的SVG類添加擴展來知足協議

謝謝@CobableKun的提醒,Demo中的類實際上是省去了實現方法。

struct SVG {
            /// XMLNode類是自定義類,這裏省去了類實現代碼。
            var rootNode = XMLNode(tag: "svg")
            mutating func append(node: XMLNode) {
    rootNode.children.append(node) }
    }

    extension SVG: Drawing {
        mutating func addEllipse(rect: CGRect, fill: UIColor) {
            var attributes: [String:String] = rect.svgAttributes attributes["fill"] = String(hexColor: fill)
            append(node: XMLNode(tag: "ellipse", attributes: attributes))
        }
        mutating func addRectangle(rect: CGRect, fill: UIColor) {
            var attributes: [String:String] = rect.svgAttributes attributes["fill"] = String(hexColor: fill)
            append(node: XMLNode(tag: "rect", attributes: attributes))
        }
    }
複製代碼

4.正式使用

var context: Drawing = SVG()
let rect1 = CGRect(x: 0, y: 0, width: 100, height: 100)
let rect2 = CGRect(x: 0, y: 0, width: 50, height: 50) 
context.addRectangle(rect: rect1, fill: .yellow) 
context.addEllipse(rect: rect2, fill: .blue)
複製代碼

協議擴展

常常看swift標準庫API的同窗確定都用見過 蘋果官方對協議擴展的使用。 這裏講講優勢: 1.不須要被強制使用某個父類

2.可讓已經存在的類型知足協議(好比咱們讓CGContext知足了Drawing)。子類就沒那麼靈活了,若是 CGContext 是一個類的話,咱們沒法以追溯的方式去變動它的父類。

3.協議既能夠用於類,也能夠用於結構體,而父類就沒法和結構體一塊兒使用了

4.當處理協議時,咱們無需擔憂方法重寫或者在正確的時間調用super這樣的問題

在協議擴展中重寫方法

沿着上面的Demo咱們再看一個使用場景。

extension SVG {
    mutating func addCircle(center: CGPoint, radius: CGFloat, fill: UIColor) {
    var attributes: [String:String] = [ "cx": "\(center.x)",
    "cy": "\(center.y)",
    "r": "\(radius)",
    ]
    attributes["fill"] = String(hexColor: fill)
    append(node: XMLNode(tag: "circle", attributes: attributes))
    } 
}
複製代碼

咱們去調用:

var sample = SVG()
sample.addCircle(center: .zero, radius: 20, fill: .red) print(sample)
/*
<svg>
<circle cy="0.0" fill="#010000" r="20.0" cx="0.0"/> </svg>
*/
複製代碼

發現正如咱們所預料的,

若是咱們把sample強轉爲Drawing

var otherSample: Drawing = SVG() otherSample.addCircle(center: .zero, radius: 20, fill: .red)
print(otherSample)
/*
<svg>
<ellipse cy="-20.0" fill="#010000" ry="40.0" rx="40.0" cx="-20.0"/> </svg>
*/
複製代碼

它返回的是 ellipse 元素,而不是咱們所指望的 circle。 當咱們將 otherSample 定義爲 Drawing 類型的變量時,編譯器會自動將 SVG 值封裝到一個表明協議的類型中,這個封裝被稱做存在容器 (existential container)

當咱們對存在容器調用 addCircle 時,方法是靜態派發

想要將 addCircle 變爲動態派發,咱們能夠將它添加到協議定義裏:

protocol Drawing {
    mutating func addEllipse(rect: CGRect, fill: UIColor)
    mutating func addRectangle(rect: CGRect, fill: UIColor)
    mutating func addCircle(center: CGPoint, radius: CGFloat, fill: UIColor)
}
複製代碼

這個時候addCircle 方法變爲了協議的一個自定義入口

相關文章
相關標籤/搜索