官方解说
面向协议编程,全称Protocol Oriented Programming,简称POP, 是 Apple 在 WWDC2015 上提出的一种编程范式,其已成为 Swift 的基础库。
在讲面向协议之前,介绍下协议的概念。
关于协议的概念,在苹果的官网是如下界说的:“协议界说了适合特定任务或功用的办法、特点和其他需求的蓝图。然后,类、结构体或枚举能够遵从该协议来供给这些需求的实践完成。任何满意协议要求的类型都被称为遵从该协议。”见 Swift 编程语言(Swift 4.0.3)部分。
这个比较难理解。我就结合个人经验,从几个方面理解下:
协议即Protocol,相似Java语言中Interface(接口),用于模块间通讯。可是又不完全相似接口。
协议在Object-C中运用的比较多,一般是结合delegate(委托),完成一个VC对另一个VC传递数据或响应事件。在此场景中的协议很像接口,界说好的一个标准。
和承继的关系,在Object-C和Swift语言中,咱们知道是不支持多重承继的,然而能够经过协议来完成多重承继。
协议与多态,本来与多态扯不上关系,可是Swift4.0.3版别之后的协议扩展(Protocol extension)又能够完成多态功用。
大白话解说
惯例的开发模式是面向对象开发,即:万物皆对象~ 任何一个Class的对象,都能够用许多特点。
假设咱们现在有两个类:Dog
和Car
,这两个类中各有一个办法run()
考虑到封装和承继,咱们能够为这两个类抽取一个父类,然后将run()
办法放于父类中.
面向对象是一种不错的笼统办法,可是必定不是最好的办法。因为狗
和轿车
底子不是同一类事物,它无法描述两个不同事物(狗和轿车)
具有某个相同特性(跑)
这一点的要求。其实利用一些特性的组合要比承继更让人接受一些。
界说一个协议
//假设只期望协议被类恪守能够在协议后边加上 :class
protocol Runable : class{
/*
*协议中既能够界说特点也能够界说办法
*留意:
1.协议中的特点和办法都不能有默许完成
2.在界说特点时,有必要清晰的指出该特点是一个可读可写/只读/只写
3.默许情况下protocol中的特点,有必要被恪守协议的类/结构体完成
*/
var speed : Int {get}
func run()
}
假设咱们期望像在OC里面相同,声明一个可选特点的协议能够这样来声明
/*
*条件:
1.有必要在protocol的前面加上@objc
2.在办法或者是特点的前面加上@objc + optional
*/
@objc protocol Runable : class{
@objc optional var speed : Int {get}
@objc optional func run()
}
假设类本身没有供给协议的完成,那么经过协议扩展供给的默许完成会被调用。
protocol Runable : class{}
//默许完成条件:有必要在协议的extension中完成
extension Runable
{
var speed : Int
{
return 20
}
func func()
{
print("正在奔跑中♀️...")
}
}
运用场景
此刻,产品经理需求咱们完成一个点赞颤动的效果。假设依照面向对象的办法,咱们有三种完成办法:
1.创立一个父类,在父类中完成颤动相关的代码。
2.创立一个UIView拓宽,在拓宽中完成相关代码。
3.运用面向协议,在协议中完成相关代码。
从完成办法来看,第一种和第二种也是能够完成功用需求的开发。
剖析办法一
办法一会存在一个代码耦合性的问题,假设在父类按钮,后续又增加了其他办法。
比如旋转又或者其他功用,那么就导致代码冗余在了父类傍边,也会对其他调用的人,造成理解的歧义。
而且假设这新功用,需求引用其他的类来完成一些功用,那么在BaseView中的代码就非常冗余,很多的代码堆积。
剖析办法二
办法二相对父类增加,确实降低了代码的耦合性问题。可是由于是拓宽办法,就导致了在任何一个UIView,都经过 .xxxx
的办法调用拓宽办法,即使他们底子不需求这个功用。假设被其他开发误用,那么也增加了其他开发者的困惑。
剖析办法三
利用 Swift的面向协议编程,咱们就能够轻松的解决这个问题。
界说好该协议而且默许完成了哆嗦的办法今后,假设控件想完成哆嗦的功用,比如当点赞按钮。
此刻咱们只需求让该按钮恪守这个协议即可,其他什么都不需求操作了,这样就能够调用lkk_shakeable()
办法即可。
import UIKit
protocol LKKShakeable{
}
extension LKKShakeable where Self : UIView
{
func lkk_shakeable()
{
translatesAutoresizingMaskIntoConstraints = true
let posLbl = layer.position
let animation = CABasicAnimation(keyPath: "position")
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
animation.fromValue = NSValue(cgPoint:CGPoint(x: posLbl.x - 10, y: posLbl.y) )
animation.fromValue = NSValue(cgPoint:CGPoint(x: posLbl.x + 10, y: posLbl.y) )
animation.autoreverses = true
animation.duration = 0.04
animation.repeatCount = 6
layer.add(animation, forKey: nil)
translatesAutoresizingMaskIntoConstraints = false
}
}
Example:
class loginButton :UIButton , LKKShakeable{}
处理Xib文件加载
加载Xib文件的这坨代码就会经常重复Copy,那么咱们能够利用面向协议的思维抽取出来,运用的当地只需求恪守该协议即可,运用起来愈加灵活.
import UIKit
protocol NibEnable {
}
extension NibLoadable where Self : UIView {
static func loadFromNib(_ nibname : String? = nil) -> Self {
let loadName = nibname == nil ? "(self)" : nibname!
return Bundle.main.loadNibNamed(loadName, owner: nil, options: nil)?.first as! Self
}
}