17.RxSwift 記憶體管理(下)
阿新 • • 發佈:2019-12-25
迴圈引用釋放不掉
self -> myClosure -> {} -> self -釋放不掉
myClosure = {() in
//// weak - strong - dance
DispatchQueue.global().asyncAfter(deadline: .now()+2,execute: {
self.name = "ABC"
print(self.name)
})
}
self.myClosure!()
複製程式碼
上面的程式碼會產生迴圈引用釋放不掉,解決辦法:
[weak self]
[unowned self]
guard let self = self else { return }
那是不是所有的閉包裡面引用self
就會產生迴圈引用?
答案是NO
,一定要構成迴圈引用圈才會
RxSwift.Resources.total
通過RxSwift.Resources.total
的引用計數檢測是否釋放:
在ViewController
裡有下列程式碼:
在HomeViewController
裡有下列程式碼:
ViewController
沒有釋放,那是因為self.title = text
[weak self]
就解決了:
RxSwift 記憶體管理常見問題
- 持有序列
-
self -> observable
->建立閉包 -> self
即create閉包引用self會產生迴圈引用 -
self -> observable
->subscribe閉包 -> self
即訂閱閉包引用self也會產生循序引用
- 觀察者持有
-
self -> disposeBag -> insert -> dispose -> sink -> observer
<-self
-
self -> observer -> observer = AnonymousObservableSink.on() -> AnonymousObservableSink._observer.on -> AnonymousObserver.on -> _eventHandler -> onNext閉包 -> self
即我們在訂閱流程裡列印self
也會產生迴圈引用
- 傳遞中的序列
會產生引用計數的不斷增加
- 交給源銷燬控制器的垃圾袋
- 序列週期伴隨控制器一起
- 不新增垃圾袋
- 如果有持有關係 需要及時通過
error
,completed
回收,如果下次想用,在進行啟用
TableViewCell
上button
點選複用的問題
let bag = DisposeBag()
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
}
複製程式碼
上面的程式碼會有什麼問題呢?
那怎麼會出現上面這種情況呢,明明已經.disposed(by: bag)
了,怎麼還會重用呢,注意:這裡的bag
的生命週期是伴隨viewController
的,這裡使用bag
自然就沒有任何效果
猜想,那使用vc
的bag
不可以,那我們可以直接使用cell
的bag
麼,如下:
//cell裡邊
var disposeBag = DisposeBag()
//vc裡邊
cell.button.rx.tap
.subscribe(onNext: { () in
print("點選了 \(indexPath)")
})
.disposed(by: cell.disposeBag)
複製程式碼
執行程式碼:
還是沒有解決,這是為啥呢,原因是我們的cell
滾動過程並沒有走deinit
,自然也就不會銷燬,因此,即便有disposeBag
,不銷燬任然也是沒有用的
那我們vc
裡重用佇列裡取cell
的地方加上cell.disposeBag = DisposeBag()
,能解決問題麼
cell.disposeBag
,滾動的過程也在不斷建立,浪費資源,浪費效能
那要怎樣才能解決該問題呢?
- 剛才這種想法是好的,那要是能換個地方初始化
cell.disposeBag
,能完美解決問題還沒有上面的問題就好了,那我們可以在cell
的內部重寫prepareForReuse
的時候初始化disposeBag
就能完美解決問題了
//cell 準備重用的方法
override func prepareForReuse() {
super.prepareForReuse()
disposeBag = DisposeBag()
}
複製程式碼
- 我們還可以使用
takeUntil
並擴充套件一個rx
的prepareForReuse
方法 來解決問題
cell.button.rx.tap.takeUntil(cell.rx.prepareForReuse)
.subscribe(onNext: { () in
print("點選了 \(indexPath)")
})
複製程式碼
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
}
// sentMessage 響應方法執行之前
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,newBag,objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
})
return bag
}
}
複製程式碼