Swift4.0 扩展协议

在OC里面中,Category大家一定不陌生,他可以很好的为我们服务,给指定的类实现系统不提供的方法。


  • 例子可能不恰当,无关紧要,重点不是OC

比如要实现:把Data转化为一个UInt8的数组
我们可能会这样子写

UInt8 *bytes = (UInt8 *)data.bytes;

现在我们不想像上面那个样子,强制类型转换,直接就是UInt8的数组,我们就可以创建一个Category,然后实现方法

.h 
- (UInt8 *)toUInt8;

.m
- (UInt8 *)toUInt8{
   return (UInt8 *)data.bytes;
}

实现了上面的方法之后,到需要的地方引入头文件就可以实现这样子调用

UInt8 *bytes = data.toUInt8;

这样子,我们就可以在实现了对NSData这个类的扩展,并且是无污染的,拖到任何一个工程就可以使用(当然,例子中的命名方式不是很靠谱,这里忽略,重点不是他)。


现在来到Swift中,如果我们也要实现如上的功能,我们可以使用Extension,也可以很方便的完成它。

新建一个Swift文件,然后实现如下代码:

extension Data {
    func toUInt8() -> [UInt8]{
        var bytes: [UInt8] = [UInt8](repeating: 0, count: count)
        copyBytes(to: &bytes, count: count)
        return bytes
    }
}

调用如下:

let data = Data(bytes: [0,1,2,3,4,5,6,7,8])
print(data.toUInt8())

打印结果:
[0, 1, 2, 3, 4, 5, 6, 7, 8]



假设你现在这个代码就写完啦,也没有问题,可以达到预想的效果了,然后新来的了一个工程师,然后他也需要把Data转化为[UInt8],但是代码是你写的,他并不知道啊。

  • 第一种情况 于是乎他又写了一个方法UInt8,实现的功能和你的一样,这样子就会有两个功能一样,只是命名方式不一样的方法了。
  • 第二种情况 他也写了一个和你命名方式一样的方法,但是他也新建了一个文件,然后你们两个方法就冲突了,他就很纳闷,黑人问号???,然后就是一顿查找……
    然后就是随着你的需求增加,你添加的方法越来越多,当你去了另外一家公司,你的这些成果本来是想着拖进去就用,但是却有了耦合,这岂不是很尴尬?



所以在Swift有了一种更优雅的扩展方法:协议扩展



接下来用一个Demo去实现它,看看他到底有多爽!!!!

正题开始啦!!!




1、 布局UI,并且他事件拖入到ViewController
image.png
2、新建一个Swift文件 PQDataEncodable.swift

这里需要注意的是:
Swift标准库为我们提供了55中协议,他们的命名方式有着自己规则,基本是以“Type”、“able'”、“Convertible”结尾,分别代表了“可以被当做XX类型”、“具备某种能力或特性”、“能够进行改变或者变换”。所以在命名的时候应该尽可能遵守这一套规则,便于开发人员之间的高校合作。

image.png

3、创建一个协议,实现它。
  • 3.1 创建一个协议先:
protocol PQDataEncodable {
    /// 关联类型
    associatedtype WarpperType
    /// 这个就是命名,我这里使用pq,你可以使用你的,比如:SnapKit,他的就是 view.snp.XXXX
    var pq: WarpperType { get }
}
  • 3.2 创建一个结构体,继承协议
struct ExtensionPQDataEncodable<T>: PQDataEncodable {
    /// T 泛型
    let pq: T
    /// 构造方法
    init(pq: T) {
        self.pq = pq
    }
}
  • 3.3 很重要的一环来了,为自己的协议添加默认实现方法
/// 这里指定 WrapperType 是 Data,所以在 PQDataEncodable 中的 pq 就是 Data 类型了
extension PQDataEncodable where WarpperType == Data {
    /// 方法名 返回参数
    func toUInt8() -> [UInt8]{
        /// 根据数组长度,创建数组
        var bytes = [UInt8](repeating: 0, count: pq.count)
        /// 把 Data 的 Bytes 拷贝到 数组中
        pq.copyBytes(to: &bytes, count: pq.count)
        /// 返回数组
        return bytes
    }
    
    func toHex() -> String {
        /// 创建一个字符串
        var hex: String = ""
        /// 遍历数组,在转换为16进制添加到字符串中
        for i in 0..<toUInt8().count {
            hex.append(NSString(format: "%02x", pq[i]) as String)
            /// 长度为4就添加一个空格, 格式化字符串
            if (i + 1) % 4 == 0 { hex.append(" ") }
        }
        /// 返回字符串
        return hex
    }
}
  • 3.4 最后一步就是,在Data中添加一个结构体,由于这个结构体是继承我自己的协议的,所以就拥有了我协议里面默认的实现方法。
extension Data {
    var pq: ExtensionPQDataEncodable<Data> {
        return ExtensionPQDataEncodable(pq: self)
    }
}




然后我们就可以在ViewController中这样子调用了

class ViewController: UIViewController {
    
    let data = Data(bytes: [0x2,0x33,0x54,0x78,0x1,0x2d,0x3a,0x5b])

    @IBAction func toHexBtnClick(_ sender: Any) {
        print(data.pq.toHex())
    }
    @IBAction func toUInt8BtnClick(_ sender: Any) {
        print(data.pq.toUInt8())
    }
    @IBAction func redBtnClick(_ sender: Any) {
    }
    @IBAction func greenBtnClick(_ sender: Any) {
    }
    @IBAction func blueBtnClick(_ sender: Any) {
    }
}

输出结果如下:

02335478 012d3a5b 012d3a5b 012d3a5b 
[2, 51, 84, 120, 1, 45, 58, 91, 1, 45, 58, 91, 1, 45, 58, 91]


这样子整个逼格就提高啦,但是你可能会有疑问,为什么要新建一个结构体呢???

我们在UIView的时候不是可以直接指定么?
例如下面的写法:

import UIKit

protocol TEST where Self : UIView {
    
}

然后我们就尝试的这样子写:

protocol TEST where Self : Data {
    
}
error

我们进入UIView,看看他的定义

NS_CLASS_AVAILABLE_IOS(2_0) @interface UIView : UIResponder <NSCoding, UIAppearance, UIAppearanceContainer, UIDynamicItem, UITraitEnvironment, UICoordinateSpace, UIFocusItem, CALayerDelegate>

在进入Data,看看他的定会

public struct Data : ReferenceConvertible, Equatable, Hashable, RandomAccessCollection, MutableCollection, RangeReplaceableCollection

一个是Class、一个是Struct,所以我们需要一个Struct来继承协议,不可以直接对原有的struct进行处理。
刚才分析到Class就可以这样子搞,那么也就是说这个Struct也可以是一个Class,然后我们就来实验一下。

4、新建一个协议 PQColorable.swift然后实现如下代码
/// 新建一个协议
protocol PQColorable {
    /// 关联类型
    associatedtype WarpperType
    var pq: WarpperType { get }
}

/// 定义一个类
final class ExtensionPQColorable<T>: PQColorable {
    /// 泛型
    let pq: T
    /// 构造方法
    init(pq: T) {
        self.pq = pq
    }
}

/// 为协议实现默认方法
extension PQColorable where WarpperType == UIColor{
    /// 获取红色
    func red() -> CGFloat {
        var value: CGFloat = 0
        pq.getRed(&value, green: nil, blue: nil, alpha: nil)
        return value
    }
    /// 获取绿色
    func green() -> CGFloat {
        var value: CGFloat = 0
        pq.getRed(nil, green: &value, blue: nil, alpha: nil)
        return value
    }
    /// 获取蓝色
    func blue() -> CGFloat {
        var value: CGFloat = 0
        pq.getRed(nil, green: nil, blue: &value, alpha: nil)
        return value
    }
}


extension UIColor{
    var pq: ExtensionPQColorable<UIColor>{
        return ExtensionPQColorable(pq: self)
    }
}



最后我们就可以在ViewController中调用啦

class ViewController: UIViewController {
    
    let data = Data(bytes: [0x2,0x33,0x54,0x78,0x1,0x2d,0x3a,0x5b,0x1,0x2d,0x3a,0x5b,0x1,0x2d,0x3a,0x5b])
    let color = #colorLiteral(red: 0.7450980544, green: 0.1568627506, blue: 0.07450980693, alpha: 1)

    @IBAction func toHexBtnClick(_ sender: Any) {
        print(data.pq.toHex())
    }
    @IBAction func toUInt8BtnClick(_ sender: Any) {
        print(data.pq.toUInt8())
    }
    @IBAction func redBtnClick(_ sender: Any) {
        print(color.pq.red())
    }
    @IBAction func greenBtnClick(_ sender: Any) {
        print(color.pq.green())
    }
    @IBAction func blueBtnClick(_ sender: Any) {
        print(color.pq.blue())
    }
}

打印结果如下:

02335478 012d3a5b 012d3a5b 012d3a5b 
[2, 51, 84, 120, 1, 45, 58, 91, 1, 45, 58, 91, 1, 45, 58, 91]
0.745098054409027
0.156862750649452
0.0745098069310188

OK,到这里应该就会如何去装逼了,使命已经达成。撤!


别走,留下DEMO

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,029评论 4 62
  • 136.泛型 泛型代码让你可以写出灵活,可重用的函数和类型,它们可以使用任何类型,受你定义的需求的约束。你可以写出...
    无沣阅读 1,452评论 0 4
  • 今天和同事,突然聊到了用手机刷公交车的问题,先暂且记录一下这个想法,改天过来继续补充细节
    Vicky_HMLiu阅读 209评论 0 0
  • 记开远市法律援助中心对周边清塘子村21家村民援助实情 2017年3月初,冬的晨露未散,所里有些稀稀冷,急碎的脚步声...
    一然梦飞阅读 312评论 0 1
  • 死亡阴影笼罩的望族 维特根斯坦家族是真正的名门望族,历史悠久资产雄厚。祖父是一位富有的犹太羊毛商。娶了维也纳银行家...
    麦麦sky阅读 1,979评论 1 3