美文网首页
RXSwift常见问题和注意事项

RXSwift常见问题和注意事项

作者: lb_ | 来源:发表于2019-08-14 18:44 被阅读0次

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()
}

相关文章

网友评论

      本文标题:RXSwift常见问题和注意事项

      本文链接:https://www.haomeiwen.com/subject/ahlnjctx.html