官方解说

面向协议编程,全称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的对象,都能够用许多特点。

假设咱们现在有两个类:DogCar,这两个类中各有一个办法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
    }
}