RxSwift内存管理

2019-08-08  本文已影响0人  silasjs

RxSwift内存管理

RxSwift 是函数响应式编程的框架。使用时满屏都是函数、闭包。那么多闭包,岂不是到处都是循环引用的陷阱?怎么来检查是否有内存泄漏呢?

RxSwift 在很多基类的构造函数和析构函数中做了埋点统计。

public class Observable<Element> : ObservableType {
    init() {
#if TRACE_RESOURCES
        _ = Resources.incrementTotal()
#endif
    }
    
    deinit {
#if TRACE_RESOURCES
        _ = Resources.decrementTotal()
#endif
    }
}

但是,需要定义TRACE_RESOURCES后才有效。在Podfile中加上如下代码即可:

post_install do |installer|
  installer.pods_project.targets.each do |target|
    if target.name == 'RxSwift'
      target.build_configurations.each do |config|
        if config.name == 'Debug'
          config.build_settings['OTHER_SWIFT_FLAGS'] ||= ['-D', 'TRACE_RESOURCES']
        end
      end
    end
  end
end

修改后记得pod update,然后就可以通过RxSwift.Resources.total来读取当前的引用计数了。

如果引用计数不平衡,就该分析代码有没循环引用的地方,循环引用无非就是我引用你,你引用我,最后扯不开的那种。这里也有闭包中的循环引用方面简单的解决方法

demo

我们先从一个简单的 demo 中分析下 RxSwift 中的持有关系:

class ViewController: UIViewController {
    var disposeBag = DisposeBag()
    var name : Any?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        Observable<Any>.create { (observer) -> Disposable in
                observer.onNext("jack")
                return Disposables.create()
            }.subscribe(onNext: { (e) in
                print(e)
            }).disposed(by: disposeBag)    
    }
}

这就是个简单的订阅,ViewController 通常都会用DisposeBag来管理订阅的生命周期。

持有关系分析

控制器会持有一个DisposeBag,而订阅中构造的内部类都有对应的Disposable来负责销毁,Disposable最后是通过disposed(by:)insertDisposeBag中的。所以,Disposable持有的,控制器都通过DisposeBag也持有了。

我们需要知道Disposable都持有了什么,才能避免在使用控制器对象的时候形成闭环引用。

序列的创建

RxSwift核心逻辑中我们知道,序列在创建的时候其实做的事情很少:

  1. 创建可观察序列AnonymousObservable
  2. 保存create闭包

这部分只有序列持有着create闭包,也就是_subscribeHandler

那么,在create闭包使用self是不会有循环引用问题的。如果self也持有着序列,就不行了。

序列的订阅

之前学习RxSwift销毁者,对销毁流程已经很熟悉了。订阅函数返回的其实是个BinaryDisposableBinaryDisposabledispose的时候,主要是在销毁sink,其他的Disposable,有闭包的,在销毁时就顺带回调一下。结合之前所学,可以得出下图的持有关系:

持有关系图.jpg

简单来说就是:

所以,在_eventHandler中使用self是很危险的,也是经常需要使用self的地方,需要破开闭环。

循环引用的问题,除了通常的弱引用外,RxSwift 也有很多种方式都可以销毁,这样循环引用的闭环也就破了:

平时开发中有很多的造成循环引用的场景,这里就不列举了,对框架源码有一定的了解,再加上程序中自己更加熟悉的代码,可以轻松避免循环引用的问题。

上一篇下一篇

猜你喜欢

热点阅读