首先普及一个概念,就是多播代理,或者叫多播委托
我们知道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,大于两个回调方法还是建议用多播代理,两个以内可以用这个
网友评论