Rx 生命周期管理

about sequence

先引用下官方文档:

Here is a sequence of numbers:

--1--2--3--4--5--6--| // terminates normally

Another sequence, with characters:

--a--b--a--a--a---d---X // terminates with error

Some sequences are finite while others are infinite, like a sequence of button taps:

---tap-tap-------tap--->

序列从生命周期来讲分为两种: 有限序列和无限序列, 有限序列以complete或者error结束,而无限序列永远都不会发送complete或者error事件。

首先明确一点观察者的生命周期肯定是要比被观察者的生命周期要长的,不然怎么观察呢,如果观察者都死掉了,被观察者还在干活,那你怎么观察呢?
对于有限序列还比较好办,在序列结束的时候发个通知告诉观察者就可以了罗。
对于无限序列呢?怎么去管理它的生命周期呢?既要保证观察者的生命周期比被观察者长,又要在恰当的时候销毁掉它。

有限序列

// AnonymousObservableSink.on

    func on(_ event: Event<E>) {
        #if DEBUG
            _synchronizationTracker.register(synchronizationErrorMessage: .default)
            defer { _synchronizationTracker.unregister() }
        #endif
        switch event {
        case .next:
            if _isStopped == 1 {
                return
            }
            forwardOn(event)
        case .error, .completed:
            if AtomicCompareAndSwap(0, 1, &_isStopped) {
                forwardOn(event)
                dispose()
            }
        }
    }


有限序列在onError或者onComplete的时候,触发dispose操作。

无限序列

无限序列就比较麻烦了,先分析下类的归属问题

// Producer.subscribe

            let disposer = SinkDisposer()
            let sinkAndSubscription = run(observer, cancel: disposer)
            disposer.setSinkAndSubscription(sink: sinkAndSubscription.sink, subscription: sinkAndSubscription.subscription)

            return disposer

// AnonymousObservable.run
    override func run<O : ObserverType>(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element {
        let sink = AnonymousObservableSink(observer: observer, cancel: cancel)
        let subscription = sink.run(self)
        return (sink: sink, subscription: subscription)
    }

SinkDisposer 持有 Sink 和 Subscription

AnonymousObservableSink持有 SinkDisposer 和 Observer

看上去有点不太理解,没关系对应下面这个具体例子:


        Observable<String>.create { observer -> Disposable in
            observer.onNext("hello")
            return Disposables.create()
        }
            .subscribe { event in
               print(event.element)
        }


SinkDisposer 持有 AnonymousObservableSink 和

{ observer -> Disposable in
            observer.onNext("hello")
            return Disposables.create()
        }

闭包产生的Disposable

AnonymousObservableSink: 持有 SinkDisposer 和

.subscribe { event in
               print(event.element)
        }

闭包产生的AnonymousObserver.

这样的话Sink持有SinkDisposer, SinkDisposer又持有Sink形成闭环,循环引用下内存永远不会释放。

验证设想

有限序列

    func testObservable()  {
       Observable<String>.create { observer -> Disposable in
            observer.onNext("hello")
            observer.onCompleted()
            return Disposables.create()
        }
            .debug()
            .subscribe { event in
               print(event.element)
        }
        defer {
            print("testObservable end")
        }
        
    }

// output log
2018-09-20 15:05:18.695: AppDelegate.swift:134 (testObservable()) -> Event next(hello)
Optional("hello")
2018-09-20 15:05:18.696: AppDelegate.swift:134 (testObservable()) -> Event completed
nil
2018-09-20 15:05:18.696: AppDelegate.swift:134 (testObservable()) -> isDisposed
testObservable end

在Debug模式下会跟踪observable的事件信息和生命周期,这里可以看到在completed情况下,成功触发dispose

无限序列

    func testObservable()  {
       Observable<String>.create { observer -> Disposable in
            observer.onNext("hello")
            return Disposables.create()
        }
            .debug()
            .subscribe { event in
               print(event.element)
        }
        defer {
            print("testObservable end")
        }
        
    }

2018-09-20 15:20:29.176: AppDelegate.swift:134 (testObservable()) -> subscribed
2018-09-20 15:20:29.177: AppDelegate.swift:134 (testObservable()) -> Event next(hello)
Optional("hello")
testObservable end

这里可以看到没有dispose,现在这种情况下已经造成内存泄漏,这块内存永远不会释放了。
这就是为什么官方推荐你使用下面这种写法的原因:

       Observable<String>.create { observer -> Disposable in
            observer.onNext("hello")
        //    observer.onCompleted()
            return Disposables.create()
        }
            .debug()
            .subscribe { event in
               print(event.element)
        }
        .disposed(by: disposeBag)

通过DisposeBag管理观察者的生命的周期,DisposeBag使用数组存放Disposable,在被释放前,会调用所有Disposable.dispose操作,达到释放内存的目的。再来看看dispose具体干了些啥:

// SinkDisposer.dispose
   func dispose() {
        let previousState = AtomicOr(DisposeState.disposed.rawValue, &_state)

        if (previousState & DisposeStateInt32.disposed.rawValue) != 0 {
            return
        }

        if (previousState & DisposeStateInt32.sinkAndSubscriptionSet.rawValue) != 0 {
            guard let sink = _sink else {
                rxFatalError("Sink not set")
            }
            guard let subscription = _subscription else {
                rxFatalError("Subscription not set")
            }

            sink.dispose()
            subscription.dispose()

            _sink = nil
            _subscription = nil
        }
    }

首先SinkDisposer 会调用 sink.dispose()
subscription.dispose() 紧接着将其置空,置空之后这个引用环就解开了,自然对象就会被释放掉。

结论

在任何时候使用subscribe都必须将其加入到DisposeBag中,否则就有可能造成内存泄漏,这样还带来一个好处你无需关注序列到底是有限序列还是无限序列,反正内存会最终释放。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,490评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,581评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,830评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,957评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,974评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,754评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,464评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,847评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,995评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,137评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,819评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,482评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,023评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,149评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,409评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,086评论 2 355

推荐阅读更多精彩内容

  • 当程序员原来越浮躁了,项目做多了大都是雷同的, 对技术没啥帮助,读一些牛逼的第三方框架,有助于提升,关于RxSwi...
    水落斜阳阅读 763评论 0 1
  • 开篇 一直觉得自己似乎越来越浮躁了,可能当代的大多数年轻人都活在恐慌里,问题已经从小时候的不思进取变成了“太思进取...
    Maru阅读 3,806评论 13 26
  • 陈温的实习工作总算结束了,还有两天才开课,忽然的无所事事,倒让陈温有些无聊,便去了图书馆,历经一个暑假,一大堆书要...
    李方知阅读 131评论 0 0
  • 【小魔仙儿】20171029学习力践行D19 我拖地的时候,她一个玩玩具,自己从书架上拿了三本以前没有看过《我会读...
    风轻云淡_adc3阅读 165评论 0 0
  • 抽象方法概念 当多个具体的实体类存在着共同的行为,但是有不同的表现,我们在父类继承过程中父类的方法具体体现不同确定...
    山不转人自转阅读 91评论 0 0