美文网首页
使用协议实现前缀效果

使用协议实现前缀效果

作者: 得_道 | 来源:发表于2020-11-08 16:27 被阅读0次

使用ReactiveCocoa过应该见过很多这样的代码,例:

textField.reactive.textValues

很多的系统类扩展了很多方法,且前面都有一个reactive的前缀效果。
这是怎么做到的呢?

分析

首先,看一个例子:

var string = "123"
print(string.mj.numbercount())

实例对象后面是.调用,所以肯定是为系统类扩展了一个属性mj,并且mj后面又调用了方法numbercount()
所以是为String类扩展了一个mj实例对象属性,假定是MJ类型, 且MJ存在一个numbercount()方法

struct MJ {
    func numberCount() -> Int {
        return 0
    }
}

extension String {
    var mj: MJ { MJ() }
}

print("123".mj.numberCount())

是不是已经出来了前缀的效果?但这只是开始...

基础版实现

我们要打印字符串个数,要改下numberCount()里的实现代码

struct MJ {
    func numberCount() -> Int {
        var count = 0
        for c in self where ("0"..."9").contains(c) {
            count += 1
        }
        return count
    }
}

但这里会报错


image.png

因为这里要遍历的是字符串,但我们怎么拿到这个要遍历的字符串呢?
需要改造下MJ这个类,增加一个string属性,并增加一个构造方法init(_ string: String),改造后如下

struct MJ {
    var string: String
    init(_ string: String) {
        self.string = string
    }
    func numberCount() -> Int {
        var count = 0
        for c in string where ("0"..."9").contains(c) {
            count += 1
        }
        return count
    }
}

然后再改变Stringmj属性, 在初始化mj时将self传进去

extension String {
    var mj: MJ { MJ(self) }
}

完整代码如下:

struct MJ {
    var string: String
    init(_ string: String) {
        self.string = string
    }
    func numberCount() -> Int {
        var count = 0
        for c in string where ("0"..."9").contains(c) {
            count += 1
        }
        return count
    }
}

extension String {
    var mj: MJ { MJ(self) }
}

print("123".mj.numberCount())
进阶版实现

假如不只给String增加mj前缀呢?还想给其它类,例如给Person增加mj前缀,也增加一个run()方法,怎么做呢?
首先给Person扩展一个mj属性

class Person {}
extension Person {
    var mj: MJ { MJ(self) }
}

MJ类要增加一个Person属性吗?

struct MJ {
    var string: String
    var person: Person
    
    init(_ string: String) {
        self.string = string
    }
    func numberCount() -> Int {
        var count = 0
        for c in string where ("0"..."9").contains(c) {
            count += 1
        }
        return count
    }

    func run() {
        print("run")
    }
}

那要很多类增加前缀,岂不是要在MJ类增加很多属性?
当然不是这样了。并且怎么分离关于StringnumberCount()Personrun()的方法呢?
配角登场 - 泛型
MJ进行泛型改造

struct MJ<Base> {
    var base: Base
    
    init(_ base: Base) {
        self.base = base
    }
}

StringPerson改造

extension String {
    var mj: MJ<String> { MJ(self) }
}

extension Person {
    var mj: MJ<Person> { MJ(self) }
}

然后把MJ的方法,该放到MJ扩展里

extension MJ where Base == String {
    func numberCount() -> Int {
        var count = 0
        for c in base where ("0"..."9").contains(c) {
            count += 1
        }
        return count
    }
}

extension MJ where Base: Person {
    func run() {
        print("run")
    }
}

上面的方法都是实例方法如果,要怎么类方法怎么做呢?
那么就要增加一个static类型的mj,同时在MJ的扩展里增加static方法

extension String {
    var mj: MJ<String> { MJ(self) }
    
    static var mj: MJ<String>.Type { MJ.self }
}

extension MJ where Base == String {
    func numberCount() -> Int {
        var count = 0
        for c in base where ("0"..."9").contains(c) {
            count += 1
        }
        return count
    }
    
    static func test() {
        print("test")
    }
}

完整代码如下:

class Person {}

struct MJ<Base> {
    var base: Base
    
    init(_ base: Base) {
        self.base = base
    }
}

extension String {
    var mj: MJ<String> { MJ(self) }
    
    static var mj: MJ<String>.Type { MJ.self }
}


extension Person {
    var mj: MJ<Person> { MJ(self) }

    static var mj: MJ<String>.Type { MJ.self }
}

extension MJ where Base == String {
    func numberCount() -> Int {
        var count = 0
        for c in base where ("0"..."9").contains(c) {
            count += 1
        }
        return count
    }
    
    static func test() {
        print("test")
    }
}

extension MJ where Base: Person {
    func run() {
        print("run")
    }
}
高级版实现

在上面的实现过程中还有存在重复的代码,如下;
这两个类的mj属性是重复的,以后要增加其他类的话,还要复制?粘贴?

extension String {
    var mj: MJ<String> { MJ(self) }
    
    static var mj: MJ<String>.Type { MJ.self }
}

extension Person {
    var mj: MJ<Person> { MJ(self) }

    static var mj: MJ<String>.Type { MJ.self }
}

这时就要用协议抽取这个公共部分, 让其他类遵循该协议

protocol MJCompatible {}
extension MJCompatible {
    
    var mj: MJ<Self> { MJ(self) }
    
    static var mj: MJ<Self>.Type { MJ.self }
}

extension String: MJCompatible {}

extension Person: MJCompatible {}

注:这里还有个细节需要注意
假如在扩展里增加一个mutating方法

extension MJ where Base == String {
    func numberCount() -> Int {
        var count = 0
        for c in base where ("0"..."9").contains(c) {
            count += 1
        }
        return count
    }
    
    static func test() {
        print("test")
    }
    
    mutating func run() {
        
    }
}

调用试试

image.png
发现报错,因为mutating意思是要修改MJ结构体里的东西,但mj是实例是作为String的只读的计算属性,所以要在协议MJCompatible的属性作如下更改,将只读属性改为可读,可写,但set什么也不做
extension MJCompatible {
    
    var mj: MJ<Self> {
        set {}
        
        get { MJ(self) }
    }
    
    static var mj: MJ<Self>.Type {
        set {}
        
        get { MJ.self }
    }
}

完整终极代码如下:

class Person {}

struct MJ<Base> {
    var base: Base
    
    init(_ base: Base) {
        self.base = base
    }
}

protocol MJCompatible {}
extension MJCompatible {
    
    var mj: MJ<Self> {
        set {}
        
        get { MJ(self) }
    }
    
    static var mj: MJ<Self>.Type {
        set {}
        
        get { MJ.self }
    }
}

extension String: MJCompatible {}

extension Person: MJCompatible {}

extension MJ where Base == String {
    func numberCount() -> Int {
        var count = 0
        for c in base where ("0"..."9").contains(c) {
            count += 1
        }
        return count
    }
    
    static func test() {
        print("test")
    }
    
    mutating func run() {
        
    }
}

extension MJ where Base: Person {
    func run() {
        print("run")
    }
}

相关文章

  • 使用协议实现前缀效果

    使用ReactiveCocoa过应该见过很多这样的代码,例: 很多的系统类扩展了很多方法,且前面都有一个react...

  • Swift - 面向协议

    Swift面向协议 利用协议实现前缀效果 定义一个结构体Lee ,用于生成计算属性 struct Lee { ...

  • Swift 实现变量方法的前缀效果

    Swift 协议实现前缀效果: 在OC中,为了区分系统的方法名,我们会给自己的方法添加前缀。Swift中也是,但是...

  • Swift5.1—可选的协议要求

    协议可以定义可选要求,遵循协议的类型可以选择是否实现这些要求。在协议中使用 optional 关键字作为前缀来定义...

  • 代理模式实现

    类图 效果 实现 使用方 遵循的协议 被代理方 代理方

  • 内网日记

    建立内网日记是为了实现交互的效果,使用的协议是比较简单的UDP协议,通过socket进行通讯功能,实现C/S 模型...

  • swift - 简单实现粒子效果

    创建一个 Swift file 文件,用协议实现粒子动画的发射和停止效果 使用方式(使用该效果的控制器,需要先遵守...

  • swift3语法(十八)

    协议 协议语法类、结构体或枚举都可以遵循协议在协议中定义类属性、方法的时候,总是使用 static 关键字作为前缀...

  • 实现TableView下拉关闭

    需要实现UIScrollViewDelegate的两个协议方法. 首先遵守协议,然后实现协议中的两个方法: 效果就...

  • iOS 应用内跳转appStore

    前言 目录:效果图实现步骤 效果图 实现步骤首先引入头文件: #import 实现协议: SK...

网友评论

      本文标题:使用协议实现前缀效果

      本文链接:https://www.haomeiwen.com/subject/aqeqbktx.html