RxSwift
讲述到此, 基本使用和核心逻辑都已经有所介绍. 那么本文就实际开发中,使用RxSwift
时经常会遇到的问题列举讲述.
RxSwift中何时需要使用 [weak self] / [unowned self]
这个内存管理的问题, 想深度了解的同学可以先阅读一下
RXSwift内存管理探索
RXSwift之Dispose销毁者解析
首先需要搞清楚的是:
当闭包里使用 self
到底会不会产生循环引用.
想解决这个问题, 建议按部就班 顺着引用链查找,直到闭包执行完毕. 如果页面/ 类中闭包较少, 可以通过查看 deinit
方法走不走来查找. 当然,在 RxSwift
的世界中, 往往走了 deinit
也不一定完全安全. 可以通过 Rx
提供的引用计数帮助查找. (比如对象销毁了,但是订阅没有销毁的情况)
举例:
var myClosure: (() -> Void)? //vc的一个属性
myClosure = {
print("\(self.name)")
}
myClosure?()
如果 myClosure
是写在外部的 VC
的一个属性. 因此 self->myClosure->self
则会造成循环引用.
反之 myClosure
只是一个方法中的临时变量.那么就完全可以在该闭包中使用 self
.
另外需要注意的是, Rxswift
中的 订阅 subscribe
本身就是循环引用, 因此,在 subscribe
中有出现 self
时,一定要使用 [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
. 但是从外部看并没有构成循环引用. 其实 subscribe
时,产生的中间类 sink
本身就是循环引用的. 当这个 sink
不释放,那么它间接持有的 self
就不会释放.
就算在最后加上 .disposed(by: self.disposeBag)
, 那么 self
的确可以释放, 但是查看引用计数会发现, 还是在不断攀升
所以我们需要使用
[weak self]
.
完整写法:
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)
总结一句话就是 当订阅闭包使用了 self
, 一定要配合 disposeBag
和 [weak self]
.
那么什么时候使用 [weak self]
, 什么时候使用 [unowned self]
呢?
记住以下几点:
unowned
访问已经释放的对象时会崩溃
weak
会打印nil
,不会崩溃.- 因此. 在确认闭包执行完成之后视图控制器/对象才会被释放时使用,其他情况使用
weak
.- 除非自己需要把异常抛出,容易查找, 使用
unowned
.
RxSwift中DisposeBag / Dispose该如何写?
这也是一个内存管理的问题.
回答:
- 一般情况我们会在一个视图控制器中定义一个
DisposeBag
的属性. 那么这个垃圾袋就会随着vc
的生命周期调用其管理对象的释放. - 当需要手动释放时, 手动将垃圾袋置为
nil
. 那么该垃圾袋管理的对象都会被释放.
Dispose
, deinit
方法的调用时机必须要掌握清楚. 不太熟悉的同学可以阅读一下
RXSwift之Dispose销毁者解析
值得注意的是,当订阅事件为一个异步任务时, 需要时刻注意订阅者在执行任务时是否会被释放掉.
举个例子, 笔者之前有一个写法, 下载一张图片, 由于需要用缓存 所以我创建了一个临时
ImageView
来使用SDWebImage
加载这张图片. 然后在其complete
回调中获取这张图片用于其他处理.
可是笔者却发现下载完成回调确死活不执行. 仔细研究才发现:
由于这个临时 ImageView
在方法执行完就会释放. 其回调当然不走了.
因此 , 笔者将此 ImageView
改为属性解决了这个问题.
RxSwift中使用 KVO 的问题
RxSwift对 KVO 的调用主要有两种方式:
rx.observe
:更加高效,因为它是一个KVO机制的简单封装。rx.observeWeakly
:执行效率要低一些,因为它要处理对象的释放防止弱引用(对象的dealloc关系)。
应用场景:
- 可以在使用
rx.observe
的地方都可以使用rx.observeWeakly
。 - 使用
rx.observe
时路径只能包括strong
属性,否则就会有系统崩溃的风险。而rx.observeWeakly
可以用在weak属性上。
RxSwift中使用 Subject / Variable 的问题
由于 Subject / Variable 既具备序列 也具备观察者的特性, 其在实际开发中经常被广泛使用. 但也正因此, 造成风险,代码可读性差,等问题.
因此, 一般在使用 Subject / Variable 要将其暴露给外部使用时,我们经常会单独暴露其一个方面给外界.
比如
我自己管理发送 ,只暴露给外部订阅的权利.
fileprivate var mySubject = PublishSubject<Any>()
var publicOB : Observable<Any>{
return mySubject.asObservable()
}
我自己管理订阅处理 ,只暴露给外部发送的权利.
fileprivate var mySubject = PublishSubject<Any>()
var ober: AnyObserver<Any>{
return mySubject.asObserver()
}