利用對象類型閉包和NSMapTable實現多播閉包

首先普及一個概念闪彼,就是多播代理,或者叫多播委托
我們知道swift回調有代理协饲、通知畏腕、kvo和閉包
項目中多對多一般就用通知了,通知是有很多缺點的茉稠,例如難以維護描馅,有些場景并不適合通知(舉個例子:各大即時通訊的消息列表,都用的是多播代理)

多播代理的原理:
普通代理只能實現一對一而线,例如controller是tableview的代理铭污,我們?yōu)榱吮苊鈨却嫘孤琩elegate都是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的字典
第二烤黍、要創(chuàng)建一個存儲閉包的類型,例如

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竿奏,大于兩個回調方法還是建議用多播代理袄简,兩個以內可以用這個

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市泛啸,隨后出現的幾起案子绿语,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吕粹,死亡現場離奇詭異种柑,居然都是意外死亡,警方通過查閱死者的電腦和手機匹耕,發(fā)現死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門聚请,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人泌神,你說我怎么就攤上這事良漱。” “怎么了欢际?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵母市,是天一觀的道長。 經常有香客問我损趋,道長患久,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任浑槽,我火速辦了婚禮蒋失,結果婚禮上,老公的妹妹穿的比我還像新娘桐玻。我一直安慰自己篙挽,他們只是感情好,可當我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布镊靴。 她就那樣靜靜地躺著铣卡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪偏竟。 梳的紋絲不亂的頭發(fā)上煮落,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天,我揣著相機與錄音踊谋,去河邊找鬼蝉仇。 笑死,一個胖子當著我的面吹牛殖蚕,可吹牛的內容都是我干的轿衔。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼睦疫,長吁一口氣:“原來是場噩夢啊……” “哼呀枢!你這毒婦竟也來了?” 一聲冷哼從身側響起笼痛,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤裙秋,失蹤者是張志新(化名)和其女友劉穎琅拌,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體摘刑,經...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡进宝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了枷恕。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片党晋。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖徐块,靈堂內的尸體忽然破棺而出未玻,到底是詐尸還是另有隱情,我是刑警寧澤胡控,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布扳剿,位于F島的核電站,受9級特大地震影響昼激,放射性物質發(fā)生泄漏庇绽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一橙困、第九天 我趴在偏房一處隱蔽的房頂上張望瞧掺。 院中可真熱鬧,春花似錦凡傅、人聲如沸辟狈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽哼转。三九已至,卻和暖如春拓春,著一層夾襖步出監(jiān)牢的瞬間释簿,已是汗流浹背亚隅。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工硼莽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人煮纵。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓懂鸵,卻偏偏與公主長得像,于是被迫代替她去往敵國和親行疏。 傳聞我的和親對象是個殘疾皇子匆光,可洞房花燭夜當晚...
    茶點故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內容

  • Swift 介紹 簡介 Swift 語言由蘋果公司在 2014 年推出,用來撰寫 OS X 和 iOS 應用程序 ...
    大L君閱讀 3,201評論 3 25
  • 用到的組件 1酿联、通過CocoaPods安裝 2终息、第三方類庫安裝 3夺巩、第三方服務 友盟社會化分享組件 友盟用戶反饋 ...
    SunnyLeong閱讀 14,606評論 1 180
  • Swift 簡介 查看Swift當前版本 簡介 Swift 語言由蘋果公司在 2014 年推出,用來撰寫 OS X...
    mian小爬閱讀 326評論 0 1
  • 隨著蘋果swift的開源,又開啟了一段新的學習旅程,不過也是必然的,那現在開始整理一下swift的筆記吧. 以...
    藍心兒的藍色之旅閱讀 2,315評論 0 4
  • 今天感恩節(jié)哎周崭,感謝一直在我身邊的親朋好友柳譬。感恩相遇!感恩不離不棄续镇。 中午開了第一次的黨會美澳,身份的轉變要...
    迷月閃星情閱讀 10,559評論 0 11