iOS實現(xiàn)多代理(swift3.0)

什么是多代理

用過環(huán)信SDK的同學(xué)應(yīng)該對多代理不陌生了,請看下面代碼:

 @method
 @brief 注冊一個監(jiān)聽對象到監(jiān)聽列表中
 @discussion 把監(jiān)聽對象添加到監(jiān)聽列表中準(zhǔn)備接收相應(yīng)的事件
 @param delegate 需要注冊的監(jiān)聽對象
 @param queue 通知監(jiān)聽對象時的線程
 @result
 */
- (void)addDelegate:(id<EMChatManagerDelegate>)delegate delegateQueue:(dispatch_queue_t)queue;

平時我們寫得比較多的代理:
@property (nonatomic,weak) id<EMChatManagerDelegate>delegate;
寫了上面屬性后系統(tǒng)會默認(rèn)生成set方法:
- (void)setDelegate:(id<EMChatManagerDelegate>)delegate;
通過對兩個接口的比較就不難看出:單代理只能設(shè)置一個,而多代理可以設(shè)置多個,準(zhǔn)確來說應(yīng)該是多代理可以添加多個

多代理有什么用

有些同學(xué)可能會問為什么要用多代理?用通知也能實現(xiàn)多個對象同時監(jiān)聽啊。是的,用監(jiān)聽通知的方式也能達(dá)到目的。

舉個例子:服務(wù)端通過 socket 傳來一個紅點(diǎn)消息{"type":21,"content":"某某消息"},
現(xiàn)在多個頁面都想拿到這個消息來判斷自己是否需要顯示紅點(diǎn)。

用通知實現(xiàn)

監(jiān)聽通知
 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onReceiveMsg:) name:@"kNotificationName_OnReceiveRedPointMsg" object:nil];
實現(xiàn)通知方法
- (void)onReceiveRedPointMsg:(NSNotification *)noti {
    NSDictionary *info = noti.userInfo;
    if ([info[@"type"] integerValue] == 21) {
        <#code#>
    }
}

用代理實現(xiàn)

注冊代理
[[RedPointManager sharedInstance] addDelegate:<#(id<RedPointManagerDelegate>)#>]
實現(xiàn)代理方法
- (void)redPointManagerDidReceive:(RedPointModel *)redPointModel {
    if (redPointModel.type == 21) {
        <#code#>
    }
}

顯然,用代理實現(xiàn)更直觀明了。

如何實現(xiàn)多代理

上面提到過setDelegate:(id<EMChatManagerDelegate>)delegate的方式是不可行的,當(dāng)?shù)诙?code>set的時候第一次設(shè)置的代理就不被持有了。只能通過addDelegate:(id<EMChatManagerDelegate>)delegate 這種方式來實現(xiàn)。

是不是有點(diǎn)不淡定了,將代理對象add到數(shù)組(或者字典)中,會使對象引用計數(shù)+1,導(dǎo)致代理對象不能釋放。沒錯,直接把代理加到數(shù)組中是不可行的。但是要持有多個代理對象,又要考慮到釋放問題怎么搞??纯雌綍r寫的代理屬性 @property (nonatomic,weak) id<EMChatManagerDelegate>delegate; 突然想到了用weak修飾不就行了嗎。

所以,可以通過橋接來實現(xiàn)對多個代理對象的持有。
這樣就好辦了,數(shù)組持有橋接對象,橋接對象再擁有自己的delegate。

class WeakObjectBridge : NSObject {
    weak var weakObject : AnyObject?
    override init() {
        super.init()
    }
    init(object:AnyObject?) {
        super.init()
        weakObject = object
    }
}

操作代理

func operatDelegate(cb: @escaping (_ delegate:AnyObject?) -> ()){
        for weakObjectBridge in self.delegateBridges {
            DispatchQueue.main.async {
                cb(weakObjectBridge.weakObject)
            }
        }
    }

具體調(diào)用

func action() {
        operatDelegate { (delegate) in
            if let myDelegate = delegate as? SomeManagerDelegate {
                myDelegate.callBack()
                myDelegate.callback?(msg: ["msg":"hello world!"])
            }
        }
    }

Demo演示

demo.gif

更新內(nèi)容

之前寫的有一個bug,在addDelegate之后,在代理對象被釋放之后,但是代理橋接對象還是存在的 ,雖然可以在代理對象的dealloc方法里手動移除代理,但我們沒必要那樣做weak修飾的屬性是不需要在dealloc里面置空的。
在參考了CYLDeallocBlockExecutor【你好 block,再見 dealloc】之后,做了如下修改:

func addDelegateObj(delegate:AnyObject?) {
        var exist = false
        for (index,weakObjectBridge) in self.delegateBridges.enumerated().reversed() {
            if let weakobj = weakObjectBridge.weakObject {
                if delegate?.isEqual(weakobj) == true {
                    exist = true
                    break
                }
            }else {
                print(index)
            }
        }
        
        if exist == false {
            let weakObjectBridge = WeakObjectBridge(object: delegate)
            let obj = delegate as! NSObject
            let deinitHandler = DeallocHandlerObject(object: weakObjectBridge)
            deinitHandler.addDeinitHandler(handler: {[weak self] (weakObjectBridge) in
                if let index = self?.delegateBridges.index(of: weakObjectBridge){
                    self?.delegateBridges.remove(at: index)
                }
            })
            obj.deinitHandler = deinitHandler
            self.delegateBridges.append(weakObjectBridge)
        }    
    }

DeallocHandlerObject 代碼如下:

import Foundation
typealias DeinitHandler = (WeakObjectBridge) -> Void
class DeallocHandlerObject: NSObject {
    var deinitHandler : DeinitHandler?
    weak var weakObjectBridge : WeakObjectBridge!
    init(object:WeakObjectBridge!) {
        super.init()
        weakObjectBridge = object
    }
    func addDeinitHandler(handler:@escaping DeinitHandler){
        deinitHandler = handler
    }
    deinit {
        deinitHandler?(weakObjectBridge)
    }
}

借助DeallocHandlerObject我們就可以知道代理對象何時被釋放了,從而達(dá)到自動移除不需要的代理橋接對象

Demo下載

點(diǎn)擊這里下載demo.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • iOS網(wǎng)絡(luò)架構(gòu)討論梳理整理中。。。 其實如果沒有APIManager這一層是沒法使用delegate的,畢竟多個單...
    yhtang閱讀 5,491評論 1 23
  • *面試心聲:其實這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,628評論 30 472
  • 1. 父類實現(xiàn)深拷貝時,子類如何實現(xiàn)深度拷貝。父類沒有實現(xiàn)深拷貝時,子類如何實現(xiàn)深度拷貝。 1.1 深拷貝同淺拷貝...
    iYeso閱讀 1,978評論 0 13
  • 1.1 什么是自動引用計數(shù) 概念:在 LLVM 編譯器中設(shè)置 ARC(Automaitc Reference Co...
    __silhouette閱讀 5,478評論 1 17
  • iOS面試小貼士 ———————————————回答好下面的足夠了------------------------...
    不言不愛閱讀 2,253評論 0 7

友情鏈接更多精彩內(nèi)容