(廿一)RxSwift之中介者模式

237 阅读2分钟

创建一个常用的Timer

 var timer: Timer?
 override func viewDidLoad() {
    super.viewDidLoad()

    timer = Timer.init(timeInterval: 1, target: self, selector: #selector(timerFire), userInfo: nil, repeats: true)
    RunLoop.current.add(timer!, forMode: .common)
}

@objc func timerFire(){
    print("timerFire")
}

deinit {
    print("deinit")
}

这段代码我们都知道因为self强引用了timer,而timertarget又强引用了self,循环引用,导致timer无法释放,不走析构函数。这时候我们思考在什么时候让timer释放最合适呢?下面有几种解决方法

第一种:didMove

当一个子对象被从父对象中移除时,父对象就会为nil,所以在这个方法中手动销毁

override func didMove(toParent parent: UIViewController?) {
    super.didMove(toParent: parent)
    
    if parent == nil {
        timer?.invalidate()
        timer = nil
    }
}

当页面离开时,我们看见timer停止了,也走了析构函数

第二种:使用Timer的block

@available(iOS 10.0, *)
public /*not inherited*/ init(timeInterval interval: TimeInterval, repeats: Bool, block: @escaping (Timer) -> Void)
override func viewDidLoad() {
    super.viewDidLoad()
    timer = Timer.init(timeInterval: 1, repeats: true, block: { (timer) in
        print("\(timer)")
    })
    RunLoop.current.add(timer!, forMode: .common)
}
deinit {
    timer?.invalidate()
    timer = nil
    print("deinit")
}

使用这个初始化方法,就会来到我们的析构函数,进而对timer进行销毁,但是我们也看见了,该方法是在iOS 10.0以后才生效。

第三种:中介者模式

self -> timer -> self, 想打破循环圈,我们可以借助中介着让target弱引用self 首先我们先来创建一个继承NSObject的类MProxy

class MProxy: NSObject {
    
    weak var target: NSObjectProtocol?
    var sel: Selector?
    var MTimer: Timer? = nil
    
    override init() {
        super.init()
    }

    func M_Timer(timeInterval ti: TimeInterval, target aTarget: Any, selector aSelector: Selector, userInfo: Any?, repeats yesOrNo: Bool) {
        
        MTimer = Timer.init(timeInterval: ti, target: self, selector: aSelector, userInfo: userInfo, repeats: yesOrNo)
        RunLoop.current.add(MTimer!, forMode: .common)
        
        self.target = aTarget as? NSObjectProtocol
        self.sel    = aSelector
        
        guard target?.responds(to: sel) == true else {
            return
        }
        
        let method = class_getInstanceMethod(classForCoder, #selector(MTimerFire))
        class_replaceMethod(classForCoder, sel!, method_getImplementation(method!),  method_getTypeEncoding(method!))
        
    }

    @objc func MTimerFire(){
        if target != nil{
            target!.perform(self.sel)
        }else{
            MTimer?.invalidate()
            MTimer = nil
        }
    }
    
    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)
        }
    }

    deinit {
        print("\(self) 走了")
    }
}
  • 首先我们声明几个属性,用来保存外界的targetsel,因为要弱引用target,所以用weak修饰。
  • 模仿系统的方法,写一个自己的Timer,内部实现仍然调用系统的。target直接引用当前类
  • 获取到外界的sel做方法做方法交换,内部实现MTimerFire方法。如果target还在,就会调用外面的方法,如果释放了就销毁当前的MTimer。 VC的调用方法
override func viewDidLoad() {
    super.viewDidLoad()
    proxy.M_Timer(timeInterval: 1, target: self, selector: #selector(timerFire), userInfo: nil, repeats: true)
}

当离开界面的时候输出:

timerFire
timerFire
timerFire
deinit
<SwiftDemo.MProxy: 0x600003386900> 走了

中介着模式使各个对象不需要显示地相互引用,降低耦合性,从而优雅的解决问题。