05. RxSwift源码解读:Connection

今天介绍可连接序列和连接相关操作符

可连接序列 Connection Observable, 不同于一般的序列,有订阅时不会立刻开始发送事件消息,只有当调用 connect()之后才会开始发送值。

connect & publish 操作符

我们看一个例子:

      let observable = Observable.create({ anyObserver in
            print("subscrition")
            anyObserver.onNext(1)
            anyObserver.onNext(2)
            anyObserver.onNext(3)
            return Disposables.create()
        })
        .publish()
        
        observable
            .subscribe {
                print("订阅1", $0)
            }
            .disposed(by: bag)
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
            _ = observable.connect()
        }
        
        observable.subscribe {
            print("订阅2", $0)
        }
subscrition
订阅1 next(1)
订阅2 next(1)
订阅1 next(2)
订阅2 next(2)
订阅1 next(3)
订阅2 next(3)

姑且把创建序列传入的闭包称之为subscribe handler

3秒后才会打印信息,因为3秒后才连接。而且我们发现subscrition只打印了一次,说明subscribe handler只执行了一遍,如果去掉publish和connect,则会执行两遍,相当于每次订阅都会重新执行一遍;如果只去掉connect,则不会发送事件。

对于上面的结果我们分析一下源码:
对于publish操作符,不妨看看:

public func publish() -> ConnectableObservable {
      self.multicast { PublishSubject() }
}

publish本质上调用了multicast操作符:

    public func multicast(makeSubject: @escaping () -> Subject)
        -> ConnectableObservable where Subject.Observer.Element == Element {
        ConnectableObservableAdapter(source: self.asObservable(), makeSubject: makeSubject)
    }

返回一个ConnectableObservableAdapter对象,它也是个Observable,保存了原始的Observable和 makeSubject闭包。

  • ConnectableObservableAdapter继承了ConnectableObservable,ConnectableObservable表示一个可连接的序列,ConnectableObservable遵循ConnectableObservableType协议声明一个connect()协议,表示连接操作。
  • SubjectType表示这是一个Subject,在上一篇文章已经讲述过,那么连接操作实际上将一个普通的Observable连接到一个Subject,而publish操作明显是接到PublishSubject,它是没有buffer的Subject。

然后再看看subscribe代码在哪:

    public func subscribe(_ on: @escaping (Event) -> Void) -> Disposable {
        let observer = AnonymousObserver { e in
            on(e)
        }
        return self.asObservable().subscribe(observer)
    }

然后调用熟悉的subscribe(observer), 这里会进入ConnectableObservableAdapter的subscribe:

    override func subscribe(_ observer: Observer) -> Disposable where Observer.Element == Subject.Element {
        self.lazySubject.subscribe(observer)
    }

这里的lazySubject是通过makeSubject闭包创建的PublishSubject();通过它继续调用subscribe,这里相当于做了一次变形,而我们在上一篇已经介绍过PublishSubject执行subscribe是不会发送事件消息的(先不考虑error和complete事件),它只是将观察者插入到observers中保存起来,这块的代码上一篇已经详细讲解过,这里不再讲解。所以就解释了为什么connect之前调用subscribe不会发送事件消息;那么connect又干了什么呢?我们继续看connect的代码:

    // at `ConnectableObservableAdapter` class
    override func connect() -> Disposable {
        return self.lock.performLocked {
            if let connection = self.connection {
                return connection
            }

            let singleAssignmentDisposable = SingleAssignmentDisposable()
            let connection = Connection(parent: self, subjectObserver: self.lazySubject.asObserver(), lock: self.lock, subscription: singleAssignmentDisposable)
            self.connection = connection
            let subscription = self.source.subscribe(connection)
            singleAssignmentDisposable.setDisposable(subscription)
            return connection
        }
    }

这里创建了一个Connection,并且执行了原始序列的subscribe(connection), 以connection作为参数,原始序列是最初的AnonymousObservable, AnonymousObservable的subscribe方法实现已经比较熟悉了,不用再看。它最终会执行最初创建序列的闭包。但是因为传入的observer是connection,而且Connection类重写了on方法,所以在调用onNext时,会调用到这个on方法:

      func on(_ event: Event) {
        if isFlagSet(self.disposed, 1) {
            return
        }
        if event.isStopEvent {
            self.dispose()
        }
        self.subjectObserver.on(event)
    }

然后执行PulishSubject的on方法:这里会向所有观察者发送消息,例子中的观察者有两个。意思是例子每次调用onNext会通知所有的观察者。这也解释了为什么“subscrition”只打印了一遍,onNext每个打印两遍。如果先connect再subscribe,则只会打印“subscrition”,因为connect时还没有观察者。

share

share操作符可接受两个参数,replay 和 scope, replay表示缓存的个数或者叫重放的次数,这个好理解,而scope可接受两个枚举值.whileConnected 和 .forever,这两个什么区别:

  • .whileConnected 只有在连接时才支持缓存和重放,意味着如果连接断开则不能重放,下次再订阅时只能重新执行subscribe handler,一般什么操作会断开连接?当发送error或complete事件或者回收资源时会断开连接。
  • .forever 永远支持缓存和重放,无论是否断开连接。
    我们看看官方给的例子:
func testScope(){
        let xs = Observable.deferred { () -> Observable in
                print("Performing work ...")
                return Observable.just(Date().timeIntervalSince1970)
            }
            .share(replay: 1, scope: .forever)

        _ = xs.subscribe(onNext: { print("next \($0)") }, onCompleted: { print("completed\n") })
        _ = xs.subscribe(onNext: { print("next \($0)") }, onCompleted: { print("completed\n") })
        _ = xs.subscribe(onNext: { print("next \($0)") }, onCompleted: { print("completed\n") })
    }

这里的just方法会发送一个单一的元素:当前时间戳,还会在发一个complete事件,scope设置的是forever
打印结果:

Performing work ...
next 1622777422.7355828
completed

next 1622777422.7355828
completed

next 1622777422.7355828
completed

第一次订阅时,执行了subscribe handler,打印了Performing work ... next和complete,之后两次订阅时是对第一次的重放,打印的时间戳也一样。我们设置重放的个数为1,所以会重放一次next,同时也会重放complete;第一次执行complete其实以及断开连接了,但是依然能重放,说明就算连接断开了依然支持重放,这就是forever的意思。
如果改成.willConnected
则无法支持重放,打印结果:

Performing work ...
next 1622778439.6327991
completed

Performing work ...
next 1622778439.6332831
completed

Performing work ...
next 1622778439.633359
completed

这里相当于执行了三遍subscre handler,所以Performing work ...打印的三次,而且3次next打印的时间戳不一样;为什么呢,因为第一订阅之后就执行了complete事件了(just操作符会执行一次onNext+一次onComplete),这时候连接断开,再次订阅时需要重新建立连接,然又会重新执行subscribe handler。
我们再看看share的源码:

      public func share(replay: Int = 0, scope: SubjectLifetimeScope = .whileConnected)
        -> Observable {
        switch scope {
        case .forever:
            switch replay {
            case 0: return self.multicast(PublishSubject()).refCount()
            default: return self.multicast(ReplaySubject.create(bufferSize: replay)).refCount()
            }
        case .whileConnected:
            switch replay {
            case 0: return ShareWhileConnected(source: self.asObservable())
            case 1: return ShareReplay1WhileConnected(source: self.asObservable())
            default: return self.multicast(makeSubject: { ReplaySubject.create(bufferSize: replay) }).refCount()
            }
        }
    }

当scope == forever时,调用的multicast, replay == 0 连接的subject是PublishSubject,这个跟publish的操作符是一样的,不一样的是后面接了一个refCount操作符。
refCount返回一个RefCount对象,并将当前对象作为source

    public func refCount() -> Observable {
        RefCount(source: self)
    }

RefCount是一个与源保持连接的可观察序列,只要该可观察序列至少有一次订阅。RefCount有一个对应的RefCountSink类,外面执行订阅时会调用RefCountSink的run方法:

 func run() -> Disposable {
        let subscription = self.parent.source.subscribe(self)
        self.parent.lock.lock(); defer { self.parent.lock.unlock() }

        self.connectionIdSnapshot = self.parent.connectionId

        if self.isDisposed {
            return Disposables.create()
        }

        if self.parent.count == 0 {
            self.parent.count = 1
            self.parent.connectableSubscription = self.parent.source.connect()
        }
        else {
            self.parent.count += 1
        }
        /// 省略。。。

第一句执行self.parent.source.subscribe(self),这里的parent是RefCount对象,而它的source是ConnectableObservableAdapter对象,它的subscribe代码:

    override func subscribe(_ observer: Observer) -> Disposable where Observer.Element == Subject.Element {
        self.lazySubject.subscribe(observer)
    }

lazySubject是要连接的Subject,之后调用subject的subscribe,这个subscribe会执行重放和插入观察者操作,回到RefCountSink的run:
后面有一个关键代码:

        if self.isDisposed {
            return Disposables.create()
        }

因为第一订阅时,会执行complete,所以执行当前dispose,第二次订阅时self.isDisposed == true,直接返回了。所以第二次第三次订阅时不会执行subscribe handler,只做重放。
如果share操作改成share(replay:2, .whileConnected), 执行的代码是return self.multicast(makeSubject: { ReplaySubject.create(bufferSize: replay) }).refCount(), 这个跟 forever分支内的 return self.multicast(ReplaySubject.create(bufferSize: replay)).refCount()代码很像,为什么一个是forever一个是whileConnected。关键在于return self.multicast(ReplaySubject.create(bufferSize: replay)).refCount()这里只会创建一个ReplaySubject对象,而 { ReplaySubject.create(bufferSize: replay) }是一个闭包,每次在执行闭包时都会创建一个新的subject。所以每次订阅时都会创建一个新的subject。新的subject不存在重放的buffer;上面的代码self.isDisposed 一直是false,这导致每次需要再次连接。而每次连接都会执行subscribe handler。

还剩下下面两个case的代码没有解读:

 case 0: return ShareWhileConnected(source: self.asObservable())
 case 1: return ShareReplay1WhileConnected(source: self.asObservable())

ShareWhileConnected 处理无buffer的情况,ShareReplay1WhileConnected处理一个buffer的情况。这两个类非常相似,这两个分支并没有连接Subject,而是用ShareReplay1WhileConnected和ShareWhileConnected代替了Subject的功能。在ShareReplay1WhileConnected和ShareWhileConnected中,保存了所有的观察者;在ShareReplay1WhileConnected中保存了最后一个元素,这是为了实现重放。
它们都持有一个Connection,分别是ShareWhileConnectedConnection和ShareReplay1WhileConnectedConnection,这两个类实现了连接:

    final func connect() {
        self.subscription.setDisposable(self.parent.source.subscribe(self))
    }

这里会去调用原始序列的subscribe,最终调用subscribe handler。
同时实现了订阅和通知观察者的功能,代码类似于Subject。

    fileprivate var observers = Observers()
    private var element: Element?

那这两个类是怎么实现whileConnected效果的? 看看ShareReplay1WhileConnected的subscribe的实现:

override func subscribe(_ observer: Observer) -> Disposable where Observer.Element == Element {
        self.lock.lock()
        let connection = self.synchronized_subscribe(observer)
        let count = connection.observers.count

        let disposable = connection.synchronized_subscribe(observer)
        self.lock.unlock()
        
        if count == 0 {
            connection.connect()
        }

        return disposable
    }

    @inline(__always)
    private func synchronized_subscribe(_ observer: Observer) -> Connection where Observer.Element == Element {
        let connection: Connection

        if let existingConnection = self.connection {
            connection = existingConnection
        }
        else {
            connection = ShareReplay1WhileConnectedConnection(
                parent: self,
                lock: self.lock)
            self.connection = connection
        }

        return connection
    }

当count == 0 执行连接,如果发送complete事件断开连接后,此时再次订阅,则count依然是==0 需要重新连接,这样就会再执行一遍subscribe handler,如果没有发送complete时间,则只会连接一次,后面的订阅只会重放buffer。

replay

replay操作符实际上是通过self.multicast { ReplaySubject.create(bufferSize: bufferSize) }实现,效果类似于publish,只不过publish不带buffer,replay可以指定buffer个数。

总结

普通可观察序列都可以通过以上操作符转变成可连接的序列,可连接序列只有在连接之后才能发送事件元素,而whileConnected和forever区别在于:

  • whileConnected:每个连接将有它自己Subject实例来存储重放事件。
  • forever:一个Suject将存储到source的所有连接的重放事件。

你可能感兴趣的:(05. RxSwift源码解读:Connection)