Swift4.0 扩展协议
2018-01-13 本文已影响665人
Codepgq
在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类型”、“具备某种能力或特性”、“能够进行改变或者变换”。所以在命名的时候应该尽可能遵守这一套规则,便于开发人员之间的高校合作。
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