RxSwift-中介者模式

proxy.png

中介者模式,顧名思義,通過中介來連接買家和供應商,減少買家和供應商的聯系成本。在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并沒有消失,那就說明當前頁面出現了循環引用,那么此處循環引用肯定是在timerself之間的。

  • self持有timertimer持有當前self構成循環引用

當然有人會想到,在deinit中給timer置空不就打破了嗎,可以嗎?當然不行,self不能釋放就不會執行deinit。那如果在頁面快消失的時候使定時器失效,并置空呢?如下:

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

打印:SecondController 銷毀

運行pushpop能夠正常銷毀,但此處會顯著很刺眼,難道我們每一個定時器都要在這個地方處理嗎。當然在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) 銷毀")
    }
}

pushpop頁面打印如下:

timer
timer
SecondController 銷毀
TimerProxy 走了 
  • 所有對象均被銷毀,由于中介者內部對當前self是弱引用,所以當前控制器能夠正常銷毀
  • 當前控制器銷毀后,proxy內部弱引用對象target也會被銷毀
  • proxy內部定時器執行判斷target時,發現targetnil即釋放了定時器
  • proxy不持有timertimer也不持有proxy,即proxy會被銷毀

RxSwift定時器

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

以上序列其實就是一個中介者,在RxSwift中管理了Timer對象,在當前對象銷毀時清除垃圾袋并銷毀定時器對象。

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,196評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,688評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,456評論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,955評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,195評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,725評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,608評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,802評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,048評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,424評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,762評論 2 372