The intermediary model, as the name implies, connects buyers and suppliers through intermediaries to reduce the cost of contact between buyers and suppliers. There are many mediators in RxSwift to help us with many things, such as MAP to help us process data and transform it into a new sequence; Filter to help us filter data and generate new sequences; Zip to help us combine multiple sequences into a single sequence. These internal complex implementations cannot be re-implemented each time they are used, and a good reuse and management can be achieved through intermediaries.

map

Observable<Int>.of(1.2.3.4.5.6).map{$0+10
    }
    .subscribe(onNext: { (val) in
        print(val)
    }).disposed(by: disposeBag)
Copy the code
  • The output is based on the original sequence element+ 10
  • 11 12 13 14 15 16 is displayed

filter

Observable<Int>.of(1.2.3.4.5).filter {$0>4}
    .subscribe(onNext: { (val) in
        print(val)
    }).disposed(by: disposeBag)
Copy the code
  • tofilterIntermediaries filter conditions, filter data
  • The output is 5

All of the above RxSwift operators are our intermediaries. Let’s look at the evolution of timer intermediaries.

Common implementation

Implement a timer and print:

self.timer = Timer.init(timeInterval: 1, target: self, selector: #selector(timerFire), userInfo: nil, repeats: true)
RunLoop.current.add(self.timer! , forMode: .common)@objc func timerFire(a) {
    print("timer")}deinit {
    print("\ [self.classForCoder)The destruction")}Copy the code

Run print, can run up, to this many people think, this is not on the line, there is a timer directly use good. Let’s check to see if the interface releases properly. Here it indicates that the current page is pushed into the page, click back to return.

If we return and find that our deinit has not disappeared, then we have a circular reference on the current page, which must be between timer and self.

  • selfholdtimer.timerHold the currentselfMake circular reference

Of course, one would think that if you set the timer null in deinit it would break, right? Of course not. If self can’t be freed, it’s not going to deinit. What if you disable the timer and empty it just as the page is about to disappear? As follows:

override func viewDidDisappear(_ animated: Bool) {
    self.timer? .invalidate()self.timer = nil
    print("viewDidDisappear")}Copy the code

Print: SecondController destroys

Running push and pop will destroy normally, but it will be noticeable here. Do we have to deal with every timer in this place? Of course, in iOS10 apple provides blocks, which only weakly reference the controller. Here’s a taste (no reference to the current controller) :

self.timer = Timer.init(timeInterval: 1, repeats: true, block: {(timer) in
    print("timer")})RunLoop.current.add(self.timer! , forMode: .common)Copy the code

Print: SecondController destroys Timer

As you can see, the current controller is destroyed, but the timer does not appear to have stopped printing, so the timer is not destroyed. In fact, the timer is held by the RunLoop. To solve this problem, we need to disable and empty the timer as above.

None of the above is convenient for us to manage the timer, and ideally the timer needs to be released with the release of the reference. We are only responsible for creating and handling the timer events. So we bring in the intermediary and leave the timer invalidation and null to the intermediary.

Broker implementation

First, the mediator needs to know that the external method selector is easy to call, and second, to break the loop reference self->timer->self, the mediator internally makes a weak reference to self. Declare the following attributes:

weak var target: NSObjectProtocol?
var sel: Selector?
var timer: Timer? = nil
Copy the code
  • Manages external objects, external selectors, and internaltimerThe timer

The Timer method is set to accept an external call object, namely a selector:

func hb_scheduledTimer(timeInterval ti: TimeInterval, target aTarget: Any, selector aSelector: Selector, userInfo: Any? , repeats yesOrNo: Bool){
    self.timer = Timer.init(timeInterval: ti, target: self, selector: #selector(hb_timerFire), userInfo: userInfo, repeats: yesOrNo)
    RunLoop.current.add(self.timer! , forMode: .common)// The external self is a weak reference here
    self.target = (aTarget as! NSObjectProtocol)
    self.sel = aSelector
}
Copy the code
  • Internal define timer, settargetIs the current intermediary class
  • Set the timer firing method to a method of the mediator class
  • Timer join toRunLoopIn the
  • Save external target objects and selectors

Focus on the mediator timer trigger method:

@objc fileprivate func hb_timerFire(a){
    if self.target ! =nil{
        self.target! .perform(self.sel)
    }else{
        self.timer? .invalidate()self.timer = nil}}deinit {
    print("\ [self.classForCoder)Go")}Copy the code
  • We have a target. PassperformCalls a method of the target object
  • No target object, that is, clear timer, removeRunLoopA reference to a timer

Now that the intermediary has been built, call the test:

class SecondController: UIViewController{
    let proxy = TimerProxy(a)override func viewDidLoad(a) {
        super.viewDidLoad()
        self.view.backgroundColor = .white
        self.proxy.hb_scheduledTimer(timeInterval: 1, target: self, selector: #selector(timerFire), userInfo: nil, repeats: true)}@objc func timerFire(a) {
        print("timer")}deinit {
        print("\ [self.classForCoder)The destruction")}}Copy the code

The push and pop page is printed as follows:

timer
timer
SecondControllerThe destructionTimerProxygoCopy the code
  • All objects are destroyed due to the intermediary internal on the currentselfIs a weak reference, so the current controller can be destroyed normally
  • After the current controller is destroyed,proxyInternally weak reference objectstargetWill be destroyed
  • proxyThe internal timer performs the judgmenttarget, they foundtargetfornilThe timer is released
  • proxyDon’t holdtimer.timerDon’t holdproxy, i.e.,proxyWill be destroyed

RxSwift timer

let timer = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
timer.subscribe(onNext: { (time) in
    print(time)
}).disposed(by: disposeBag)
Copy the code

The sequence above is essentially a mediator that manages the Timer object in RxSwift, cleans up the garbage bag and destroys the Timer object when the current object is destroyed.

This is the role of the mediator object, directly used, do not have to release the project timer operation. In fact, the intermediary is to encapsulate complex and tedious operations and businesses that are not easy to manage, simplify the operation process, and make the development more concise and efficient.