RxSwift
講述到此, 基本使用和核心邏輯都已經(jīng)有所介紹. 那么本文就實(shí)際開(kāi)發(fā)中,使用RxSwift
時(shí)經(jīng)常會(huì)遇到的問(wèn)題列舉講述.
RxSwift中何時(shí)需要使用 [weak self] / [unowned self]
這個(gè)內(nèi)存管理的問(wèn)題, 想深度了解的同學(xué)可以先閱讀一下
RXSwift內(nèi)存管理探索
RXSwift之Dispose銷(xiāo)毀者解析
首先需要搞清楚的是:
當(dāng)閉包里使用 self
到底會(huì)不會(huì)產(chǎn)生循環(huán)引用.
想解決這個(gè)問(wèn)題, 建議按部就班 順著引用鏈查找,直到閉包執(zhí)行完畢. 如果頁(yè)面/ 類(lèi)中閉包較少, 可以通過(guò)查看 deinit
方法走不走來(lái)查找. 當(dāng)然,在 RxSwift
的世界中, 往往走了 deinit
也不一定完全安全. 可以通過(guò) Rx
提供的引用計(jì)數(shù)幫助查找. (比如對(duì)象銷(xiāo)毀了,但是訂閱沒(méi)有銷(xiāo)毀的情況)
舉例:
var myClosure: (() -> Void)? //vc的一個(gè)屬性
myClosure = {
print("\(self.name)")
}
myClosure?()
如果 myClosure
是寫(xiě)在外部的 VC
的一個(gè)屬性. 因此 self->myClosure->self
則會(huì)造成循環(huán)引用.
反之 myClosure
只是一個(gè)方法中的臨時(shí)變量.那么就完全可以在該閉包中使用 self
.
另外需要注意的是, Rxswift
中的 訂閱 subscribe
本身就是循環(huán)引用, 因此,在 subscribe
中有出現(xiàn) self
時(shí),一定要使用 [weak self]
例子:
Observable<Any>.create { (anyObserver) -> Disposable in
anyObserver.onNext("Hello word")
return Disposables.create()
}
.subscribe(onNext: { (item) in
print(self)
print("訂閱到:\(item)")
})
以上案例中 subscribe
閉包持有 self
. 但是從外部看并沒(méi)有構(gòu)成循環(huán)引用. 其實(shí) subscribe
時(shí),產(chǎn)生的中間類(lèi) sink
本身就是循環(huán)引用的. 當(dāng)這個(gè) sink
不釋放,那么它間接持有的 self
就不會(huì)釋放.
就算在最后加上 .disposed(by: self.disposeBag)
, 那么 self
的確可以釋放, 但是查看引用計(jì)數(shù)會(huì)發(fā)現(xiàn), 還是在不斷攀升
所以我們需要使用
[weak self]
.
完整寫(xiě)法:
Observable<Any>.create { (anyObserver) -> Disposable in
self.observer = anyObserver
anyObserver.onNext("Hello word")
return Disposables.create()
}
.subscribe(onNext: {[weak self] (item) in
print(self)
print("訂閱到:\(item)")
})
.disposed(by: self.disposeBag)
總結(jié)一句話就是 當(dāng)訂閱閉包使用了 self
, 一定要配合 disposeBag
和 [weak self]
.
那么什么時(shí)候使用 [weak self]
, 什么時(shí)候使用 [unowned self]
呢?
記住以下幾點(diǎn):
unowned
訪問(wèn)已經(jīng)釋放的對(duì)象時(shí)會(huì)崩潰
weak
會(huì)打印nil
,不會(huì)崩潰.- 因此. 在確認(rèn)閉包執(zhí)行完成之后視圖控制器/對(duì)象才會(huì)被釋放時(shí)使用,其他情況使用
weak
.- 除非自己需要把異常拋出,容易查找, 使用
unowned
.
RxSwift中DisposeBag / Dispose該如何寫(xiě)?
這也是一個(gè)內(nèi)存管理的問(wèn)題.
回答:
- 一般情況我們會(huì)在一個(gè)視圖控制器中定義一個(gè)
DisposeBag
的屬性. 那么這個(gè)垃圾袋就會(huì)隨著vc
的生命周期調(diào)用其管理對(duì)象的釋放. - 當(dāng)需要手動(dòng)釋放時(shí), 手動(dòng)將垃圾袋置為
nil
. 那么該垃圾袋管理的對(duì)象都會(huì)被釋放.
Dispose
, deinit
方法的調(diào)用時(shí)機(jī)必須要掌握清楚. 不太熟悉的同學(xué)可以閱讀一下
RXSwift之Dispose銷(xiāo)毀者解析
值得注意的是,當(dāng)訂閱事件為一個(gè)異步任務(wù)時(shí), 需要時(shí)刻注意訂閱者在執(zhí)行任務(wù)時(shí)是否會(huì)被釋放掉.
舉個(gè)例子, 筆者之前有一個(gè)寫(xiě)法, 下載一張圖片, 由于需要用緩存 所以我創(chuàng)建了一個(gè)臨時(shí)
ImageView
來(lái)使用SDWebImage
加載這張圖片. 然后在其complete
回調(diào)中獲取這張圖片用于其他處理.
可是筆者卻發(fā)現(xiàn)下載完成回調(diào)確死活不執(zhí)行. 仔細(xì)研究才發(fā)現(xiàn):
由于這個(gè)臨時(shí) ImageView
在方法執(zhí)行完就會(huì)釋放. 其回調(diào)當(dāng)然不走了.
因此 , 筆者將此 ImageView
改為屬性解決了這個(gè)問(wèn)題.
RxSwift中使用 KVO 的問(wèn)題
RxSwift對(duì) KVO 的調(diào)用主要有兩種方式:
rx.observe
:更加高效,因?yàn)樗且粋€(gè)KVO機(jī)制的簡(jiǎn)單封裝斩郎。rx.observeWeakly
:執(zhí)行效率要低一些脑融,因?yàn)樗幚韺?duì)象的釋放防止弱引用(對(duì)象的dealloc關(guān)系)。
應(yīng)用場(chǎng)景:
- 可以在使用
rx.observe
的地方都可以使用rx.observeWeakly
缩宜。 - 使用
rx.observe
時(shí)路徑只能包括strong
屬性肘迎,否則就會(huì)有系統(tǒng)崩潰的風(fēng)險(xiǎn)。而rx.observeWeakly
可以用在weak屬性上。
RxSwift中使用 Subject / Variable 的問(wèn)題
由于 Subject / Variable 既具備序列 也具備觀察者的特性, 其在實(shí)際開(kāi)發(fā)中經(jīng)常被廣泛使用. 但也正因此, 造成風(fēng)險(xiǎn),代碼可讀性差,等問(wèn)題.
因此, 一般在使用 Subject / Variable 要將其暴露給外部使用時(shí),我們經(jīng)常會(huì)單獨(dú)暴露其一個(gè)方面給外界.
比如
我自己管理發(fā)送 ,只暴露給外部訂閱的權(quán)利.
fileprivate var mySubject = PublishSubject<Any>()
var publicOB : Observable<Any>{
return mySubject.asObservable()
}
我自己管理訂閱處理 ,只暴露給外部發(fā)送的權(quán)利.
fileprivate var mySubject = PublishSubject<Any>()
var ober: AnyObserver<Any>{
return mySubject.asObserver()
}