就問此時此刻還有誰奋救?45度仰望天空,該死反惕!我這無處安放的魅力尝艘!
- RxSwift(1)—— 初探
- RxSwift(2)—— 核心邏輯源碼分析
- RxSwift(3)—— Observable序列的創(chuàng)建方式
- RxSwift(4)—— 高階函數(shù)(上)
- RxSwift(5)—— 高階函數(shù)(下)
- RxSwift(6)—— scheduler源碼解析(上)
- RxSwift(7)—— scheduler源碼解析(下)
- RxSwift(8)—— KVO底層探索(上)
- RxSwift(9)—— KVO底層探索(下)
- RxSwift(10)—— 場景序列總結
- RxSwift(11)—— 銷毀者-dispose源碼解析
- RxSwift(12)—— Subject即攻也守
- RxSwift(13)—— 爬過的坑
- RxSwift(14)—— MVVM雙向綁定
RxSwift目錄直通車--- 和諧學習,不急不躁姿染!
RxSwift
是一個非常好用的框架背亥,如果你喜歡用Swift
開發(fā),那么RxSwift
是你不二的選擇,函數(shù)響應式的結果狡汉,讓你的代碼飛起來娄徊!在上癮RxSwift
給我們帶來的便捷的同時,經常也會出現(xiàn)一些致命的坑盾戴,讓你怎么也爬不出去寄锐,難受的一匹....歸其本質:你還是對RxSwift
不夠了解片仿,如果你想玩好RxSwift
杀餐,不妨花點時間靜下心來研究一下底層颁糟!這一篇文章給大家介紹幾點遵馆,平時在使用RxSwift
經常會遇到的坑
RxSwift計數(shù)問題
首先有兩個頁面LGHomeViewController 首頁
和LGDetialViewController 詳情
,詳情頁面給首頁進行傳值,我們可以通過序列傳遞芍阎,達到你意想不到的快感躁绸,看代碼
LGDetialViewController 中
// 內部序列響應吮铭,不被外界影響
fileprivate var mySubject = PublishSubject<Any>()
var publicOB : Observable<Any>{
return mySubject.asObservable()
}
- 對外暴露
publicOB
,以便訂閱信息 -
mySubject
序列響應矛渴,內部事件發(fā)送 - 內外區(qū)分,達到干凈的效果
LGHomeViewController 中
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let vc = LGDetialViewController()
vc.publicOB
.subscribe(onNext: { (item) in
print("訂閱到 \(item)")
})
.disposed(by: disposeBag)
self.navigationController?.pushViewController(vc, animated: true)
}
- 首頁響應
push
到詳情頁面去 - 訂閱內部響應事件惫搏,以便處理事務
上面的代碼乍一看沒有什么問題具温,其實不然,這個過程不斷往首頁disposeBag
添加訂閱事件筐赔,會導致計數(shù)不斷增加铣猩,就是性能消耗
*****LGDetialViewController出現(xiàn)了:RxSwift的引用計數(shù): 37
****************************************
<_01_RxSwift-內存管理.LGDetialViewController: 0x7fa966414de0>走了
銷毀了
<_01_RxSwift-內存管理.LGPerson: 0x600001c8c6c0>銷毀釋放
text = Optional("Cooci")
*****LGDetialViewController出現(xiàn)了:RxSwift的引用計數(shù): 38
****************************************
<_01_RxSwift-內存管理.LGDetialViewController: 0x7fa96665d280>走了
銷毀了
<_01_RxSwift-內存管理.LGPerson: 0x600001c93780>銷毀釋放
text = Optional("Cooci")
*****LGDetialViewController出現(xiàn)了:RxSwift的引用計數(shù): 39
****************************************
- 可以清晰的看到:計數(shù)由37-38-39
解決辦法
- 思路一:最直接不加入垃圾銷毀袋
-
思路二:我們分析因為,當前首頁沒有釋放導致首頁的銷毀垃圾袋不斷增多茴丰!
換一個垃圾銷毀袋
-
思路三: 我們通過前面的銷毀流程达皿,可以直接調用
complete
或者error
信號達到 :及時回收,這樣就不至于一直存在首頁的垃圾袋中
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// 頁面要退出,及時完成以便調用dispose
mySubject.onCompleted()
}
因為調用序列的完成函數(shù)贿肩,就會導致我們的序列一次性峦椰,下次就無法響應
解決辦法:重新激活
fileprivate var mySubject = PublishSubject<Any>()
var publicOB : Observable<Any>{
// 重置激活
mySubject = PublishSubject<Any>()
return mySubject.asObservable()
}
cell復用導致序列重復訂閱響應
我們實際開發(fā)中避免不了使用tableView
,那么在使用過程中,經常會有一個坑:cell復用
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) as! LGTableViewCell
cell.button.rx.tap
.subscribe(onNext: { () in
print("點擊了 \(indexPath)")
})
.disposed(by: bag)
return cell
}
- 這一段代碼乍一看也是沒有什么問題的汰规,包括你不滑動屏幕也不會產生問題
- 只要你一劃動屏幕汤功,因為我們的
cell
的重用機制,會導致cell.button.rx.tap
的訂閱也會重復訂閱響應溜哮,顯然不是我們正常開發(fā)中想見到的樣子
點擊了 [0, 0]
********************
點擊了 [0, 1]
********************
點擊了 [0, 2]
********************
點擊了 [0, 1]
點擊了 [0, 21]
********************
點擊了 [0, 3]
點擊了 [0, 23]
********************
點擊了 [0, 29]
點擊了 [0, 49]
點擊了 [0, 69]
********************
解決思路
-
思路一: 把主動銷毀的能力收回滔金,銷毀垃圾袋交給我們的
cell.disposeBag
,在我們重用響應的時候,及時銷毀茂嗓,重置餐茵!
// 外界訂閱處理
cell.button.rx.tap
.subscribe(onNext: { () in
print("點擊了 \(indexPath)")
})
.disposed(by: cell.disposeBag)
// cell內部處理
override func prepareForReuse() {
super.prepareForReuse()
// 銷毀垃圾袋重置
disposeBag = DisposeBag()
}
銷毀垃圾袋交給
cell
自身在
prepareForReuse
響應的時候,銷毀垃圾袋重置效果很明顯述吸,問題得到了解決忿族!
思路二:基類封裝
class LGCustomCell: UITableViewCell{
var disposeBag = DisposeBag()
override func prepareForReuse() {
super.prepareForReuse()
disposeBag = DisposeBag()
}
}
- 把相關重用方法和我們垃圾袋封裝在基類,這樣就大大節(jié)省人力咯!
作為一個牛逼的開發(fā)人員肠阱,每每想到在
tableView
中處理響應都需要重寫prepareForReuse
,我就覺得難受票唆,此刻我要勇敢的說:RxSwift
其實你可以更好
于是我?guī)е粚⒕偷男膽B(tài)構建我們的思路三和思路四
extension Reactive where Base: UITableViewCell {
// 提供給外界重用序列
public var prepareForReuse: RxSwift.Observable<Void> {
var prepareForReuseKey: Int8 = 0
if let prepareForReuseOB = objc_getAssociatedObject(base, &prepareForReuseKey) as? Observable<Void> {
return prepareForReuseOB
}
let prepareForReuseOB = Observable.of(
sentMessage(#selector(Base.prepareForReuse)).map { _ in }
, deallocated)
.merge()
objc_setAssociatedObject(base, &prepareForReuseKey, prepareForReuseOB, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
return prepareForReuseOB
}
// 提供一個重用垃圾回收袋
public var reuseBag: DisposeBag {
MainScheduler.ensureExecutingOnScheduler()
var prepareForReuseBag: Int8 = 0
if let bag = objc_getAssociatedObject(base, &prepareForReuseBag) as? DisposeBag {
return bag
}
let bag = DisposeBag()
objc_setAssociatedObject(base, &prepareForReuseBag, bag, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
_ = sentMessage(#selector(Base.prepareForReuse))
.subscribe(onNext: { [weak base] _ in
let newBag = DisposeBag()
guard let base = base else {return}
objc_setAssociatedObject(base, &prepareForReuseBag, newBag, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
})
return bag
}
}
- 重用響應
prepareForReuse
,只要發(fā)現(xiàn)我們的cell
已發(fā)生重用,通過RxSwift
就會接受到一個重用序列的響應屹徘,起到綁定效果 - 這里還合并了
cell
的銷毀序列走趋,畢竟cell
都銷毀了也就沒有任何響應的意義
cell.button.rx.tap.takeUntil(cell.rx.prepareForReuse)
.subscribe(onNext: { () in
print("點擊了 \(indexPath)")
})
- 通過
takeUntil
限定了button
的點擊響應能力
cell.button.rx.tap
.subscribe(onNext: { () in
print("點擊了 \(indexPath)")
})
.disposed(by: cell.rx.reuseBag)
-
思路四:就是通過把此次響應加入到特定的銷毀袋,這個銷毀袋通過關聯(lián)屬性的方式保證了一定的性能噪伊,同時這個銷毀袋是觀察了
cell
的重寫響應簿煌,一旦有重寫那么就直接銷毀重置,達到自動重置效果 - 思路三&思路四都是基于最初的思路不夠構建鉴吹,目的也是為了:Write once, run anywhere
2019年08月10日 01:33
還在堅持把博客寫完姨伟,這一年一直在不斷更新博客內容(因為之前一直忙還有自己惰性都沒有好好更新)看到很多博主都是粉絲幾千,內心也難免失落豆励。悟已往之不諫夺荒,知來者之可追! 接下來會持續(xù)努力和大家一起共建iOS
生態(tài)強盛良蒸。我還是我技扼,顏色不一樣的煙火,我是Cooci
,我為自己帶鹽嫩痰!就問此時此刻還有誰剿吻?45度仰望天空,該死串纺!我這無處安放的魅力丽旅!