美文网首页RxSwiftRx
04. RxSwift源码解读:Subject

04. RxSwift源码解读:Subject

作者: Oceanj | 来源:发表于2021-06-08 14:11 被阅读0次

    今天带大家解读下Subject相关类的源码。
    在我们之前讲过的类中,有些类是观察者,有些是被观察者,今天要说的Subject比较特殊,它既是观察者,又是被观察者,兼具两者的特性。

    Subject相关类包括:PublishSubject, BehaviorSubject, AsyncSubject, ReplaySubject

    还是先了解基本用法,请看下面的例子:

        private let bag = DisposeBag()
        private let subject = PublishSubject<Int>()
        override func viewDidLoad() {
            super.viewDidLoad()
            subject.onNext(1)
            subject.subscribe(onNext: { ele in
                print("First ->",ele)
            })
            .disposed(by: bag)
            subject.onNext(2)
            subject.subscribe(onNext: { ele in
                print("Second ->",ele)
            })
            .disposed(by: bag)
            subject.onNext(3)
    }
    

    先定义一个subject = PublishSubject<Int>(), subject既可以发送序列,也可以订阅序列,它先后发送1,2,3,同时订阅两次,打印结果为:

    First -> 2
    First -> 3
    Second -> 3
    

    我们看到1没有打印,2打印一次 3打印两次,为什么呢?
    PublishSubject只会接受订阅之后的元素,1在订阅之前发送,所以无法接受到元素。2在第一次订阅之后发送,所以收到第一次订阅的元素,3在两次订阅之后发送,所以两次订阅都接受到元素;我们通过源码分析下内部原理:

    PublishSubject

    因为Subject既是观察者又是被观察者,所以我们看看它的类的定义:

    public final class PublishSubject<Element>
        : Observable<Element>
        , SubjectType
        , Cancelable
        , ObserverType
        , SynchronizedUnsubscribeType
    

    其他3种Subject也是一样:首先继承了Observable,然后遵循了ObserverType,同时又遵循Cancelable,所以不是两者是三者兼具。另外还遵循了SubjectType,SubjectType继承了ObservableType,它有个协议方法asObserver(), 返回一个ObserverType, 一般返回自身对象:

        /// Returns observer interface for subject.
        public func asObserver() -> PublishSubject<Element> {
            self
        }
    

    PublishSubject作为Observer,实现了on协议方法, 我们知道当observer调用onNext等方法时,会调用on方法:

        /// Notifies all subscribed observers about next event.
        ///
        /// - parameter event: Event to send to the observers.
        public func on(_ event: Event<Element>) {
            #if DEBUG
                self.synchronizationTracker.register(synchronizationErrorMessage: .default)
                defer { self.synchronizationTracker.unregister() }
            #endif
            dispatch(self.synchronized_on(event), event)
        }
    

    synchronizationTracker.registersynchronizationTracker.unregister, 这两个是为了保证不会出现重入问题,即在当前on方法执行完成前,又执行了on方法,这会导致循环调用。
    然后调用了synchronized_on:

        func synchronized_on(_ event: Event<Element>) -> Observers {
            self.lock.lock(); defer { self.lock.unlock() }
            switch event {
            case .next:
                if self.isDisposed || self.stopped {
                    return Observers()
                }
                
                return self.observers
            case .completed, .error:
                if self.stoppedEvent == nil {
                    self.stoppedEvent = event
                    self.stopped = true
                    let observers = self.observers
                    self.observers.removeAll()
                    return observers
                }
    
                return Observers()
            }
        }
    

    这里是线程安全的,而且用的递归锁实现的,我想可能是为了保证锁能嵌套,因为可以会出现方法嵌套调用。
    如果未释放资源,或者未停止,则直接返回一个Observers(),否则返回当前observers,这个Observers是重点,它其实是一个Bag:Bag<(Event<Element>) -> Void>, item类型是一个闭包。这个闭包是发送元素的闭包,Bag内部保存了这些闭包。Bag的内部实现其实是这样的:将第一个元素放在_key0 和 _value0中,key是通过自增的方式产生唯一的key。如果超过一个元素 则将其他元素存放在数组中,数组最大空间为30,超过30个元素则将多余的放在字典里,所以这个bag内部有一个数组和一个字典,这样设计的好处是当元素个数较少时(少于30),数组的查找效率不会低,同时空间使用率较高,超过30个数组查找效率会变低适合用字典,而字典的查找效率可以达到O1的效率,但空间使用率较低,所以才这么设计,大家可以自行查看Bag结构体的源码。我们可以把observers当成一个观察者容器,存放了所有观察者。

    所以返回Observers(),相当于创建一个新Bag对象,所以内部没有元素。

    当synchronized_on 返回observers后,接着执行dispatch方法:

    func dispatch<Element>(_ bag: Bag<(Event<Element>) -> Void>, _ event: Event<Element>) {
        bag._value0?(event)
    
        if bag._onlyFastPath {
            return
        }
    
        let pairs = bag._pairs
        for i in 0 ..< pairs.count {
            pairs[i].value(event)
        }
    
        if let dictionary = bag._dictionary {
            for element in dictionary.values {
                element(event)
            }
        }
    }
    

    这个方法目的就取出所有闭包,并执行这些闭包,相当于发送序列。
    这里先执行第一个元素的闭包(如果有的话),_onlyFastPath表示当元素只有一个时,则_onlyFastPath等于true,就是说当只有一个元素时,后面无需再执行了,有多个元素时还需要遍历数组和字典执行闭包,发出序列。
    如果事件类型是.completed, .error, 那么将stopped 设置 true, stoppedEvent设置为event, 返回当前observers并清空observers,后续当然无法再接收事件.
    stoppedEvent 设置完后,后面再订阅时会将这个event发出去,这个event一定是error或completed,我们可以看看订阅的代码:

          func synchronized_subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
            if let stoppedEvent = self.stoppedEvent {
                observer.on(stoppedEvent)
                return Disposables.create()
            }
            
            if self.isDisposed {
                observer.on(.error(RxError.disposed(object: self)))
                return Disposables.create()
            }
            
            let key = self.observers.insert(observer.on)
            return SubscriptionDisposable(owner: self, key: key)
        }
    

    如果已经释放资源了,则发送error.
    否则将observer的on方法放入observers,缓存起来。等到发送序列时再把所有缓存的Observer发出去;每订阅一次就会缓存一个。这就是为什么在订阅之前发送序列无效,因为没有在缓存中,只要在发送序列之前订阅了,怎么这些订阅都会收到这个序列。

    然后再看看释放资源的代码:

        public func dispose() {
            self.lock.performLocked { self.synchronized_dispose() }
        }
    
        final func synchronized_dispose() {
            self.disposed = true
            self.observers.removeAll()
            self.stoppedEvent = nil
        }
    

    很简单先加锁,然后移除所有observers, disposed= true, stoppedEvent = nil;
    所以释放资源后,所有观察者无法在接收到序列了。

    现在试着在例子中加入error和complete事件:

            subject.onNext(1)
            subject.subscribe(onNext: { ele in
                print("First ->",ele)
            }, onError: { error in
                print(error)
            }, onCompleted: {
                print("complete")
            })
            .disposed(by: bag)
            subject.onNext(2)
            subject.subscribe(onNext: { ele in
                print("Second ->",ele)
            }, onError: { error in
                print(error)
            }, onCompleted: {
                print("complete")
            }).disposed(by: bag)
            
            subject.onNext(3)
            subject.onError(RxError.unknown)
            subject.onCompleted()
            subject.subscribe(onNext: { ele in
                print("Third ->",ele)
            }, onError: { error in
                print(error)
            }, onCompleted: {
                print("complete")
            }).disposed(by: bag)
    

    打印出:

    First -> 2
    First -> 3
    Second -> 3
    Unknown error occurred.
    Unknown error occurred.
    Unknown error occurred.
    

    当订阅error时,也会将发送序列的闭包缓存到observers中,这样在调用onError时,两个观察者都会收到error事件,所以打印前两条error,之后在发送onCompleted事件时,因为在发送onError时会清空observers,所以这个时候观察者什么都收不到,之后又订阅了一次,这时因为stoppedEvent != nil, 所以直接调用on发送之前的error事件,所以会再打印一次error。

    BehaviorSubject

    BehaviorSubject的代码跟PublishSubject基本相同,只是BehaviorSubject会保存最后一次发送的元素。

     private var element: Element
    

    BehaviorSubject初始化时,会给这个element初始化。然后订阅时插入observer.on的同时会把这个element发出去。

            let key = self.observers.insert(observer.on)
            observer.on(.next(self.element))
    

    发送onNext时会更新这个element。

             case .next(let element):
                self.element = element
    

    AsyncSubject

    AsyncSubject会发出Observable发出的最后一个值(也仅是最后一个值),并且只有在这个Observable完成之后;(如果Observable没有发出任何值,那么AsyncSubject也会在没有发出任何值的情况下完成。),完成事件发出后,后续无法再接受事件,error事件也算完成事件。
    考虑下面的例子:

    subject.onNext(1)
            subject.subscribe(onNext: { ele in
                print("First ->",ele)
            }, onError: { error in
                print("First ->", error)
            }, onCompleted: {
                print("complete")
            })
            .disposed(by: bag)
            subject.onNext(2)
            subject.subscribe(onNext: { ele in
                print("Second ->",ele)
            }, onError: { error in
                print("Second ->", error)
            }, onCompleted: {
                print("complete")
            }).disposed(by: bag)
            
            subject.onNext(3)
    //        subject.onError(RxError.unknown)
            subject.onCompleted()
            
            subject.subscribe(onNext: { ele in
                print("Third ->",ele)
            }, onError: { error in
                print("Third ->", error)
            }, onCompleted: {
                print("complete")
            }).disposed(by: bag)
    

    这时候只会发出3,同时complete事件也会发出,打印结果:

    First -> 3
    Second -> 3
    complete
    complete
    Third -> 3
    complete
    

    onCompleted之后再次订阅,会收到最后的元素和完成事件。
    如果打开// subject.onError(RxError.unknown)的注释,则只会打印error事件:

    First -> Unknown error occurred.
    Second -> Unknown error occurred.
    Third -> Unknown error occurred.
    

    error之后,事件已经完成,再发送onCompleted则无效,所以不会打印complete。
    有一点跟BehaviorSubject很像就是在发出完成事件之后再订阅依然可以收到最后一个元素和完成事件。
    在源码中subject保存了最后一个元素(lastElement),发出error时,会立刻向所有观察者发出error事件,同时将stoppedEvent赋值为error事件,之后再也无法发出事件:

            if self.isStopped {
                return (Observers(), .completed)
            }
    

    如果没有发出error, 而是发出了complete事件,怎么判断是否有lastElement,如果有则向所有观察者发出最后一个元素和complete事件,如果没有lastElement,怎么只发出complete事件,之后清空观察者。stoppedEvent赋值为最后一个next事件。
    在订阅的代码中:

        func synchronized_subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
            if let stoppedEvent = self.stoppedEvent {
                switch stoppedEvent {
                case .next:
                    observer.on(stoppedEvent)
                    observer.on(.completed)
                case .completed:
                    observer.on(stoppedEvent)
                case .error:
                    observer.on(stoppedEvent)
                }
                return Disposables.create()
            }
    
            let key = self.observers.insert(observer.on)
    
            return SubscriptionDisposable(owner: self, key: key)
        }
    

    如果已经完成(前面已经发出了error或complete),则发出最后一个error,或最后一个元素和complete,或者只发出complete。
    否则将观察者插入到observers中。

    ReplaySubject

    我们把PublishSubject理解为没有缓存的序列,BehaviorSuject缓存一个元素,而ReplaySubject可以缓存任意个元素,根据缓存策略,每项通知都会广播给所有已订阅和未来的观察者。
    实现方式是:ReplayOne缓存一个元素,ReplayManyBase缓存多个元素,通过队列维护这些元素,发出缓存元素时,遍历队列发送元素。(先进先出)

        override func replayBuffer<Observer: ObserverType>(_ observer: Observer) where Observer.Element == Element {
            for item in self.queue {
                observer.on(.next(item))
            }
        }
    

    当buffer的数量超出设置的数量时,进行出队操作

        override func trim() {
            while self.queue.count > self.bufferSize {
                _ = self.queue.dequeue()
            }
        }
    

    ReplayManyBase继承自ReplayBufferBase,ReplayBufferBase中封装了核心逻辑,发送数据时先缓存元素,再向观察者发出元素,订阅时先重放缓存的元素,再保存观察者。

    总结

    Suject因为既是Observable,ObserverType,同时又时Cancelable,核心逻辑基本都在一个类中,相对来说代码比较简单。它实现了一个典型的观察者模式(先保存所有的观察者,发送事件时通知所有观察者)。

    相关文章

      网友评论

        本文标题:04. RxSwift源码解读:Subject

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