swift 中的協議仍是很酷的
😎node
1.能夠當作代理
來使用。編程
2.可讓結構體
,枚舉
來知足協議。swift
3.還能夠經過協議的extension
爲協議添加新方法。app
4.協議容許咱們動態派發
。svg
5.OC中共享代碼一般使用的繼承,swift中能夠經過使用的是協議來共享代碼
。spa
6.你能夠爲你的類添加協議去達到功能點整合
。代理
這一小節舉了個例子來介紹面相協議編程的幾個使用場景
。code
將 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)
}
}
複製代碼
謝謝@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 方法變爲了協議的一個自定義入口