美文网首页
RxSwift-中介者模式(Timer)

RxSwift-中介者模式(Timer)

作者: May_Dobin | 来源:发表于2019-08-12 15:38 被阅读0次

    中介者顾名思义就是一个桥梁,通过中介者使对象间解耦。
    首先看一下定时器Timer循环引用问题无法释放,下面的代码:

    class ViewController: UIViewController {  
        var mTimer: Timer?    
        override func viewDidLoad() {
            super.viewDidLoad()
            mTimer = Timer.init(timeInterval: 1, target: proxy, selector: #selector(timerFire), userInfo: nil, repeats: true)
            RunLoop.current.add(mTimer!, forMode: .common)
        }
        @objc func timerFire(){
            print("timer fire")
        }
        deinit {
            print("\(self) 走了")
        }
    }
    

    上面的代码,在我们让ViewController“销毁”后,实际由于mTimerself的强引用,Timer会继续执行,而ViewController并没有销毁,那么我们如何解决呢?
    很庆幸,在iOS 10之后苹果爸爸引入了新的方法,通过下面的方法初始化计时器,就不会产生循环引用:

            mTimer = Timer.init(timeInterval: 1, repeats: true, block: { (timer) in
                print("timer fire \(timer)")
            })
            RunLoop.current.add(mTimer!, forMode: .common)
    

    那么问题来了,iOS 10之前的系统,如何解决接下来,我们引入中介者类——MyProxy。在这个类中有两种处理方式:

    第一种

    MyProxy使用self作为target,#selector(timerIsGo)作为selector初始化计时器,在timerIsGo中做容错处理

    class MyProxy: NSObject {
    
        weak var target : NSObjectProtocol?
        var sel: Selector?
        var timer: Timer?
        override init() {
            super.init()
        }
        func my_scheduledTimer(timeInterval time: TimeInterval, target aTarget: Any, selector aSelector: Selector, userInfo: Any?, repeats aRepeats: Bool){
            
            self.timer = Timer(timeInterval: time, target: self as Any, selector: #selector(timerIsGo), userInfo: userInfo, repeats: aRepeats)
            RunLoop.current.add(self.timer!, forMode: .common)
            self.target = aTarget as? NSObjectProtocol
            self.sel = aSelector
        }
        @objc fileprivate func timerIsGo(){
            if self.target != nil {
                //此处如果做了如下判断,外界调用时
                //如果是通过这种形式传入方法 let selector = NSSelectorFromString("timerFire")
                //如果漏写了方法实现,很难定位timer 不响应的问题
                if self.target?.responds(to: self.sel) == true {
                    self.target!.perform(self.sel)
                }else{
                    print("\(self.target!) 中没有实现\(self.sel!)")
                    self.timer?.invalidate()
                    self.timer = nil
                }
            }else{
                self.timer?.invalidate()
                self.timer = nil
            }
        }
        deinit {
            print("\(self) 走了")
        }
    

    外界调用如下:

            let selector = NSSelectorFromString("timerFire")
            myProxy.my_scheduledTimer(timeInterval: 1, target: self, selector: selector, userInfo: nil, repeats: true)
    

    特别注意:

    • 现在的引用链为self ->proxy -weak->target -> self,并没有产生强循环引用
    • MyProxy中初始化计时器时用的targetMyProxy,而不是传入的aTarget
    • 通过self.target保存原类ViewController,self.sel保存原来的timerFire方法
    • 通过MyProxy中的timer不断调用timerIsGo(),间接响应原类self.target中的self.sel方法
    • timerIsGo ()中判断target是否存在,不存在就销毁计时器
    第二种

    MyProxy依然使用self作为target,使用传进来的aSelector作为selector初始化计时器,通过runtime进行方法交换,响应为自定义的方法timerIsGo(),在消息转发时做容错处理;详见代码:

            func my_scheduledTimer(timeInterval time: TimeInterval, target aTarget: Any, selector aSelector: Selector, userInfo: Any?, repeats aRepeats: Bool){
            
            self.timer = Timer(timeInterval: time, target: self, selector: aSelector, userInfo: userInfo, repeats: aRepeats)
            RunLoop.current.add(self.timer!, forMode: .common)
            self.target = aTarget as? NSObjectProtocol
            self.sel = aSelector
            
            guard self.target?.responds(to: self.sel) == true else{
                return
            }
            let method = class_getInstanceMethod(self.classForCoder, #selector(timerIsGo))
            class_replaceMethod(self.classForCoder, self.sel!, method_getImplementation(method!), method_getTypeEncoding(method!))
        }
        @objc fileprivate func timerIsGo(){
            if self.target != nil {
                self.target!.perform(self.sel)
            }else{
                self.timer?.invalidate()
                self.timer = nil
            }
        }
        deinit {
            print("\(self) 走了")
        }
        override func forwardingTarget(for aSelector: Selector!) -> Any? {
            if self.target?.responds(to: self.sel) == true {
                return self.target
            }
            else{
                print("在此处理异常")
                return super.forwardingTarget(for: aSelector)
            }
        }
    

    代码分析:

            guard self.target?.responds(to: self.sel) == true else{
                return
            }
            let method = class_getInstanceMethod(self.classForCoder, #selector(timerIsGo))
            class_replaceMethod(self.classForCoder, self.sel!, method_getImplementation(method!), method_getTypeEncoding(method!))
    
    • 如果外界没有实现selector方法,直接return,下面的class_replaceMethod方法交换就不会执行
    • 找不到方法实现,计时器就不会有响应,然后通过runtime的消息查找机制,最终会调用forwardingTarget进行消息转发:
        override func forwardingTarget(for aSelector: Selector!) -> Any? {
            if self.target?.responds(to: self.sel) == true {
                return self.target
            }
            else{
                print("在此处理异常")
                return super.forwardingTarget(for: aSelector)
            }
        }
    

    我们直接抛出异常,当然如果想要工程更加完美,可以在此手动添加方法实现,做出响应处理。

    相关文章

      网友评论

          本文标题:RxSwift-中介者模式(Timer)

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