天天看點

RxSwift-中介者模式

RxSwift-中介者模式

中介者模式,顧名思義,通過中介來連接配接買家和供應商,減少買家和供應商的聯系成本。在

RxSwift

中存在很多中介者來幫我們處理很多事情,如

map

來幫我們處理資料并轉化為新的序列;

filter

來幫我們篩選資料并産生新序列;

zip

來幫助我們将多個序列合成為一個序列。這些内部複雜的實作不可能每次在用到時重新實作一邊,通過中介者達到一個很好的複用及管理。

map

Observable<Int>.of(1,2,3,4,5,6)
    .map{
        $0+10
    }
    .subscribe(onNext: { (val) in
        print(val)
    }).disposed(by: disposeBag)
           
  • 輸出結果在原有序列元素的基礎上

    +10

  • 輸出結果為:11 12 13 14 15 16

filter

Observable<Int>.of(1,2,3,4,5)
    .filter {$0>4}
    .subscribe(onNext: { (val) in
        print(val)
    }).disposed(by: disposeBag)
           
  • filter

    中介者篩選條件,篩選資料
  • 輸出結果為:5

以上RxSwift操作符均是我們的中介者,下面我們來看一下定時器中介者的演化。

普通實作

實作一個定時器并列印:

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() {
    print("timer")
}

deinit {
    print("\(self.classForCoder) 銷毀")
}
           

運作列印,能夠跑起來,到這很多人覺得,這不就行了嗎,有定時器直接用就好。我們來檢查一下,界面是否能夠正常釋放。這裡說明目前頁面是

push

進來的頁面,點選傳回即可。

傳回後發現我們的

deinit

并沒有消失,那就說明目前頁面出現了循環引用,那麼此處循環引用肯定是在

timer

self

之間的。

  • self

    持有

    timer

    timer

    持有目前

    self

    構成循環引用

當然有人會想到,在

deinit

中給

timer

置空不就打破了嗎,可以嗎?當然不行,

self

不能釋放就不會執行

deinit

。那如果在頁面快消失的時候使定時器失效,并置空呢?如下:

override func viewDidDisappear(_ animated: Bool) {
    self.timer?.invalidate()
    self.timer = nil
    print("viewDidDisappear")
}
           
列印:SecondController 銷毀

運作

push

pop

能夠正常銷毀,但此處會顯着很刺眼,難道我們每一個定時器都要在這個地方處理嗎。當然在

iOS10

系統中蘋果提供了

block

,隻需弱引用控制器即可。下面來感受一下(此處沒有引用目前控制器):

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

列印:

SecondController 銷毀

timer

我們可以看到,目前控制器被銷毀了,但是定時器好像并沒有停止列印,是以這裡定時器并沒有銷毀,其實定時器是被

RunLoop

所持有,為解決這一問題,我們還是需要像上面那樣使定時器失效并置空才能解決。

以上方法都不便于我們對定時器的管理,而我們理想中的定時器需要跟随引用者的釋放而釋放,我們隻負責建立和處理定時器事件。那麼我們就引入中介者,把

timer

的失效和置空交給中介者解決就行。

中介者實作

首先中介者要知道外界的方法選擇器便于調用,再者為打破

self->timer->self

的循環引用,中介者内部對

self

做弱引用。聲明屬性如下:

weak var target: NSObjectProtocol?
var sel: Selector?
var timer: Timer? = nil
           
  • 管理外界對象,外界選擇器,及内部的

    timer

    定時器

仿造

Timer

定時器方法,對外設定定時器方法接受外部調用對象即選擇器:

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)
    //此處對外self是弱引用
    self.target = (aTarget as! NSObjectProtocol)
    self.sel = aSelector
}
           
  • 内部定義定時器,設定

    target

    為目前中介者類
  • 設定定時器觸發方法為中介者類的方法
  • 定時器加入到

    RunLoop

  • 儲存外界目标對象及選擇器

重點在中介者定時器觸發方法中:

@objc fileprivate func hb_timerFire(){
    if self.target != nil{
        self.target!.perform(self.sel)
    }else{
        self.timer?.invalidate()
        self.timer = nil
    }
}
deinit {
    print("\(self.classForCoder) 走了 ")
}
           
  • 有目标對象,通過

    perform

    調用目标對象的方法
  • 沒有目标對象,即清除定時器,解除

    RunLoop

    對定時器的引用

以上中介者已建構完成,下面調用測試一下:

class SecondController: UIViewController{
    let proxy = TimerProxy()

    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = .white
        self.proxy.hb_scheduledTimer(timeInterval: 1, target: self, selector: #selector(timerFire), userInfo: nil, repeats: true)
    }
    @objc func timerFire() {
        print("timer")
    }
    deinit {
        print("\(self.classForCoder) 銷毀")
    }
}
           

push

pop

頁面列印如下:

timer
timer
SecondController 銷毀
TimerProxy 走了 
           
  • 所有對象均被銷毀,由于中介者内部對目前

    self

    是弱引用,是以目前控制器能夠正常銷毀
  • 目前控制器銷毀後,

    proxy

    内部弱引用對象

    target

    也會被銷毀
  • proxy

    内部定時器執行判斷

    target

    時,發現

    target

    nil

    即釋放了定時器
  • proxy

    不持有

    timer

    timer

    也不持有

    proxy

    ,即

    proxy

    會被銷毀

RxSwift定時器

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

以上序列其實就是一個中介者,在

RxSwift

中管理了

Timer

對象,在目前對象銷毀時清除垃圾袋并銷毀定時器對象。

以上就是中介者對象的作用,直接使用,不用再對項目中定時器做釋放操作。中介者其實就是封裝複雜繁瑣的操作,和不便于管理的業務,簡化操作流程,讓開發變得更簡潔更高效。