美文网首页
利用对象类型闭包和NSMapTable实现多播闭包

利用对象类型闭包和NSMapTable实现多播闭包

作者: 码农淏 | 来源:发表于2021-03-13 01:02 被阅读0次

首先普及一个概念,就是多播代理,或者叫多播委托
我们知道swift回调有代理、通知、kvo和闭包
项目中多对多一般就用通知了,通知是有很多缺点的,例如难以维护,有些场景并不适合通知(举个例子:各大即时通讯的消息列表,都用的是多播代理)

多播代理的原理:
普通代理只能实现一对一,例如controller是tableview的代理,我们为了避免内存泄漏,delegate都是weak的。而多代理就是拿一个weak类型的数组把delegate存起来,这个数组就是NSHashTable
代码:

@objc
protocol AnimalPrtocol {
    func eat()
    func sleep()
}

class Controller: UIViewController {

// 没用过的注意options的weakMemory,资料很多不做赘述
       private let hashTable = NSHashTable<NSObject>.init(options: .weakMemory)
// 添加代理
       public func addDelegate(_ delegate: AnimalPrtocol) {
        hashTable.add(delegate)
      }

// 调用代理方法,饿了去吃
    private func hungry() {
        hashTable.allObjects.forEach { $0.eat() }
    }
}

已上就实现了多代理

而如果想把这个代理改成闭包呢?

第一、NSHashTable可以实现一个weak的数组,NSMapTable可以实现一个weak的字典
第二、要创建一个存储闭包的类型,例如

class Block {
  var stringCallback: ((String) -> ())?
  var intCallback: ((Int) -> ())?
  var modelCallback: ((XXXModel) -> ())?
}

缺点很明显,需要设置无数个回调
这里推荐使用王巍大大的Delegate类型
使用 protocol 和 callAsFunction 改进 Delegate
https://onevcat.com/2020/03/improve-delegate/
具体内容可以去看,本人不做赘述
以下是demo, 就这么三五十行。。要看效果的新建项目复制粘贴快很多.也可以去下载看看
https://gitee.com/nameUser/multicast-closure


import UIKit

class Model: NSObject {
    
    var name: String = ""
    convenience init(name: String) {
        self.init()
        self.name = name
    }
}

class ViewController: UIViewController {
    // 这里的valueOptions如果不在这里存直接释放所以用了strong,当key释放时,value自动释放。valueOptions根据自己是否保存过去选择weak或者strong,一般闭包只在这里引用一次
    var mapTable = NSMapTable<Model, Delegate<String, Void>>.init(keyOptions: .weakMemory, valueOptions: .strongMemory)
    var models: [Model] = []

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        for i in 0..<10 {
            let model = Model.init(name: "\(i)")
            models.append(model)
            let comp: Delegate<String, Void> = .init()
            mapTable.setObject(comp, forKey: model)
        }
        
        for object in mapTable.keyEnumerator() {
            if let objc = object as? Model {
                mapTable.object(forKey: objc)?.delegate(on: self, closure: { (self, string) -> Void in
                    print(string)
                })
            }
        }
// 也可以直接取 value
        /*
         mapTable.objectEnumerator()?.allObjects.forEach {
            if let value = $0 as? Delegate<String, Void> {
                value.delegate(on: self, closure: { (self, string) -> Void in
                    print(string)
                })
            }
         }
         */
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        models.removeFirst()
        for object in mapTable.keyEnumerator() {
            if let objc = object as? Model {
                mapTable.object(forKey: objc)?.callAsFunction(objc.name)
            }
        }
        print(mapTable)
    }

}

以下是王巍大大的Delegate整合

import Foundation

public class Delegate<Input, Output> {
    
    public init() {}
    
    private var closure: ((Input) -> Output?)?
    
    /// 委托回调
    /// - Parameters:
    ///   - target: 目标对象
    ///   - closure: 回调闭包
    public func delegate<T: AnyObject>(on target: T, closure: @escaping ((T, Input) -> Output)) {
        // The `target` is weak inside block, so you do not need to worry about it in the caller side.
        self.closure = { [weak target] input in
            guard let target = target else { return nil }
            return closure(target, input)
        }
    }
    
    /// 取消代理回调
    public func cancell() {
        self.closure = nil
    }

    @discardableResult
    public func callAsFunction(_ input: Input) -> Output? {
        return closure?(input)
    }
}

extension Delegate where Input == Void {
    
    public func delegate<T: AnyObject>(on target: T, closure: @escaping ((T) -> Output)) {
        // The `target` is weak inside block, so you do not need to worry about it in the caller side.
        self.closure = { [weak target] _ in
            guard let target = target else { return nil }
            return closure(target)
        }
    }
    
    @discardableResult
    public func callAsFunction() -> Output? {
        return closure?(())
    }
}

public protocol OptionalProtocol {
    static var Nil: Self { get }
}

extension Optional: OptionalProtocol {
    
    public static var Nil: Optional<Wrapped> {
        return nil
    }
}

extension Delegate where Output: OptionalProtocol {
    
    @discardableResult
    public func callAsFunction(_ input: Input) -> Output {
        switch closure?(input) {
        case .some(let value):
            return value
            
        case .none:
            return .Nil
        }
    }
}

缺点就是每加一个方法就要加一个NSMapTable去记录,去callAsFunction,大于两个回调方法还是建议用多播代理,两个以内可以用这个

相关文章

  • 利用对象类型闭包和NSMapTable实现多播闭包

    首先普及一个概念,就是多播代理,或者叫多播委托我们知道swift回调有代理、通知、kvo和闭包项目中多对多一般就用...

  • JavaScript - 闭包

    理解 关于闭包 答案: 用arguments.callee和闭包实现的函数封装 应用 利用闭包实现自动递增计数

  • 06.实现一个累加器

    使用构造函数和对象的方式,实现累加器:本质上还是闭包 使用闭包实现一个累加器闭包

  • 闭包

    闭包的基本语法 闭包的内容捕获 闭包和函数的引用类型

  • swift- 闭包一

    /*• 闭包表达式• 尾随闭包• 值捕获• 闭包是引用类型• 逃逸闭包• 自动闭包*/

  • 闭包

    闭包 本节内容包括: 闭包表达式 尾随闭包 值捕获 闭包是引用类型 Swift 中的闭包与 C 和 Objecti...

  • Swift 2 学习笔记 9.闭包

    课程来自慕课网liuyubobobo老师 闭包 闭包的基本语法 闭包语法的简化 结尾闭包 内容捕获 闭包和函数是引用类型

  • Day7 闭包(Closures)

    本页包含内容:• 闭包表达式• 尾随闭包• 值捕获• 闭包是引用类型• 逃逸闭包• 自动闭包 1、闭包表达式 闭包...

  • Swift: @escaping 和 @noescaping 是

    @noescaping: 非逃逸闭包类型@escaping: 逃逸闭包类型 Swift 3.0 之后,传递闭包...

  • Swift基础之闭包

    闭包 Swift对闭包进行了简化: 利用上下文推断参数和返回值类型 隐式返回单表达式闭包,即单表达式闭包可以省略r...

网友评论

      本文标题:利用对象类型闭包和NSMapTable实现多播闭包

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