中介者模式,顧名思義,通過中介來連接買家和供應商,減少買家和供應商的聯系成本。在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
對象,在當前對象銷毀時清除垃圾袋并銷毀定時器對象。
以上就是中介者對象的作用,直接使用,不用再對項目中定時器做釋放操作。中介者其實就是封裝復雜繁瑣的操作,和不便于管理的業務,簡化操作流程,讓開發變得更簡潔更高效。