RXSwift中 Subject的使用和分析

SubjectRxSwift 中一种特殊的序列, 最重要的特征是 它即为可观察序列, 也为观察者. 在实际开发中也最为常用,因此建议多了解其逻辑原理,对实际开发使用时有很大帮助。

为什么它是双重身份呢?
废话不多说,上代码

/// Represents an object that is both an observable sequence as well as an observer.
///
/// Each notification is broadcasted to all subscribed observers.
public final class PublishSubject<Element>
    : Observable<Element>
    , SubjectType
    , Cancelable
    , ObserverType
    , SynchronizedUnsubscribeType 

它继承于 ObservableObserverType , 因此 他既有 subscribe 功能,也有 on(_ event: Event<E>) 功能

PublishSubject

  • 使用案例
// 1:初始化序列
let publishSub = PublishSubject<Int>() //初始化一个PublishSubject 装着Int类型的序列
// 2:发送响应序列
publishSub.onNext(1)
// 3:订阅序列
publishSub.subscribe { print("订阅到了:",$0)}
    .disposed(by: disposbag)
// 再次发送响应
publishSub.onNext(2)
publishSub.onNext(3)
  • 打印结果 :

订阅到了: next(2)
订阅到了: next(3)

  • 分析:
    cmd + 点击 PublishSubject 进入该类, 找到 on 方法,中间继承链查找方法流程不在多赘述
public func on(_ event: Event<Element>) {
    #if DEBUG
        self._synchronizationTracker.register(synchronizationErrorMessage: .default)
        defer { self._synchronizationTracker.unregister() }
    #endif
    dispatch(self._synchronized_on(event), event)
}

先来看 self._synchronized_on(event)

func _synchronized_on(_ event: Event<E>) -> 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()
    }
}

返回的是当前 PublishSubjectObservers ,一个 Bag对象,可以理解为数组, 也就是当前观察者数组.

再点击 dispatch 找到如下方法:

func dispatch<E>(_ bag: Bag<(Event<E>) -> Void>, _ event: Event<E>) {
    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)
        }
    }
}

可以看到,该方法是循环自己保存的所有观察者,调用发送 event ,在第一次执行 publishSub.onNext(1) 时, 并没有观察者, 因此并无响应.

则回到代码中 下一步

publishSub.subscribe { print("订阅到了:",$0)}

进入 PublishSubject 类寻找 subscribe 方法
过度方法:

public override func subscribe<O: ObserverType>(_ observer: O) -> Disposable where O.E == Element {
    self._lock.lock()
    let subscription = self._synchronized_subscribe(observer)
    self._lock.unlock()
    return subscription
}

再进入 _synchronized_subscribe

func _synchronized_subscribe<O: ObserverType>(_ observer: O) -> Disposable where O.E == E {
    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)
}

可以明显看到 订阅一次,就在观察者数组中插入这个观察者.

因此, PublishSubject 在未订阅时,所发送的 event是没有响应的, 后续发送 会查找所有观察者, 循环遍历, 一一发送响应.

BehaviorSubject

  • 使用案例
let behaviorSub = BehaviorSubject.init(value: 100)
// 2:发送信号
behaviorSub.onNext(2)
behaviorSub.onNext(3)
// 3:订阅序列
behaviorSub.subscribe{ print("订阅到了:",$0)}
    .disposed(by: disposbag)
// 再次发送
behaviorSub.onNext(4)
behaviorSub.onNext(5)
// 再次订阅
behaviorSub.subscribe{ print("订阅到了:",$0)}
    .disposed(by: disposbag)
  • 打印结果 :

订阅到了: next(3)
订阅到了: next(4)
订阅到了: next(5)
订阅到了: next(5)

注释掉 behaviorSub.onNext(2)behaviorSub.onNext(3)
打印结果:

订阅到了: next(100)
订阅到了: next(4)
订阅到了: next(5)
订阅到了: next(5)

  • 分析 :
public init(value: Element) {
    self._element = value

    #if TRACE_RESOURCES
        _ = Resources.incrementTotal()
    #endif
}

初始化时,保存了传进来的默认值到自己的 _element 属性中.
后续每次发送 behaviorSub.onNext(2),来到如下方法

public func on(_ event: Event<E>) {
    dispatch(self._synchronized_on(event), event)
}

func _synchronized_on(_ event: Event<E>) -> Observers {

    switch event {
    case .next(let element):
        self._element = element
    case .error, .completed:
        self._stoppedEvent = event
    }
    
    return self._observers
}

可以看到,跟 PublishSubject 基本差不多,除了给所有观察者发送事件以外,多了一步

self._element = element

也就是保存了最新发送的 element

同样,subscribe 方法:

public override func subscribe<O: ObserverType>(_ observer: O) -> Disposable where O.E == Element {
    self._lock.lock()
    let subscription = self._synchronized_subscribe(observer)
    self._lock.unlock()
    return subscription
}

func _synchronized_subscribe<O: ObserverType>(_ observer: O) -> Disposable where O.E == E {
    
    let key = self._observers.insert(observer.on)
    observer.on(.next(self._element))

    return SubscriptionDisposable(owner: self, key: key)
}

subscribe 方法也是跟 PublishSubject 基本一致,除了把此次观察者添加保存,多了一步

observer.on(.next(self._element))

也就是说在订阅时 会默认发送一次 on next 事件,发送内容为保存的最新 element

ReplaySubject

  • 使用案例
    // 1:创建序列
    let replaySub = ReplaySubject<Int>.create(bufferSize: 2)
    // let replaySub = ReplaySubject<Int>.createUnbounded()
    
    // 2:发送信号
    replaySub.onNext(1)
    replaySub.onNext(2)
    replaySub.onNext(3)
    replaySub.onNext(4)
    
    // 3:订阅序列
    let disposbag = DisposeBag()
    replaySub.subscribe{ print("订阅到了:",$0)}
        .disposed(by: disposbag)
    // 再次发送
    replaySub.onNext(7)
    replaySub.onNext(8)
    replaySub.onNext(9)

  • 打印结果 :

订阅到了: next(3)
订阅到了: next(4)
订阅到了: next(7)
订阅到了: next(8)
订阅到了: next(9)

  • 分析
    ReplaySubjectBehaviorSubject 其实实现原理基本类似,只不过后者是用一个 _element 属性来记录最新一次的 event 值,而前者在给定的 bufferSize 大于1时使用 _queue 来存储一组 Element,等于1时 使用一个 _value 属性来记录最新一次的 event 值。

AsyncSubject

  • 使用案例
    // AsyncSubject
    // 1:创建序列
    let asynSub = AsyncSubject<Int>.init()
    // 2:发送信号
    asynSub.onNext(1)
    asynSub.onNext(2)
    // 3:订阅序列
    let disposbag = DisposeBag()
    asynSub.subscribe{ print("订阅到了:",$0)}
        .disposed(by: disposbag)
    // 再次发送
    asynSub.onNext(3)
    asynSub.onNext(4)
//        asynSub.onError(NSError.init(domain: "lb", code: 10086, userInfo: nil))
    asynSub.onCompleted()
  • 打印结果

订阅到了: next(4)
订阅到了: completed

打开注释掉的 onError这一行

  • 打印结果

订阅到了: error(Error Domain=lb Code=10086 "(null)")

  • 分析
    直接查看源码
public func on(_ event: Event<E>) {
    #if DEBUG
        self._synchronizationTracker.register(synchronizationErrorMessage: .default)
        defer { self._synchronizationTracker.unregister() }
    #endif
    let (observers, event) = self._synchronized_on(event)
    switch event {
    case .next:
        dispatch(observers, event)
        dispatch(observers, .completed)
    case .completed:
        dispatch(observers, event)
    case .error:
        dispatch(observers, event)
    }
}

继续点击 _synchronized_on 方法

func _synchronized_on(_ event: Event<E>) -> (Observers, Event<E>) {
    
    switch event {
    case .next(let element):
        self._lastElement = element
        return (Observers(), .completed)
    case .error:
        self._stoppedEvent = event

        let observers = self._observers
        self._observers.removeAll()

        return (observers, event)
    case .completed:

        let observers = self._observers
        self._observers.removeAll()

        if let lastElement = self._lastElement {
            self._stoppedEvent = .next(lastElement)
            return (observers, .next(lastElement))
        }
        else {
            self._stoppedEvent = event
            return (observers, .completed)
        }
    }
}

上面可以看到其实 AsyncSubject 在接收到 on(_ event: Event<E>) 事件时 只有在接收到 .completed 事件时,才会自动发送一次 .next(lastElement)AsyncSubject 在实际开发中使用的并不多。

AsyncSubject 在完成时发出最后一个元素,如果源 Observable 没有发出任何元素,只有一个完成事件,则 AsyncSubject 也只有一个完成事件。如果源 Observable 产生了一个 error 事件而中止,那么 AsyncSubject 就不会发出任何元素,而是将 error 事件发送出来。

Variable

Variable 在5.0版本中已经废弃,使用( BehaviorSubject 或者 BehaviorRelay 替换)

  • 使用案例
    let variableSub = Variable.init(1)
    // 2:发送信号
    variableSub.value = 100
    variableSub.value = 10
    // 3:订阅信号
    let disposbag = DisposeBag()
    variableSub.asObservable().subscribe{ print("订阅到了:",$0)}
        .disposed(by: disposbag)
    // 再次发送
    variableSub.value = 1000
  • 打印结果:

订阅到了: next(10)
订阅到了: next(1000)

  • 分析

从源码可以看出,Variable 虽然没有继承自 ObserverType 或者 Observable 。但是其有一个 _subject: BehaviorSubject<Element> 属性。所以,Variable 的行为和 BehaviorSubject 是一致的。但因为不是继承自 ObserverType,所以没有 on 函数,不能直接调用 on 函数发送信号。

在初始化时,使用初始化值,初始化 BehaviorSubject,并保存在 self._subject 中。
value 做了一层封装,在 valueset 函数中,会调用 _subjecton 函数。完成信号的发送。

public typealias E = Element

    private let _subject: BehaviorSubject<Element>

    // state
    private var _value: E

    public var value: E {
        get {
            self._lock.lock(); defer { self._lock.unlock() }
            return self._value
        }
        set(newValue) {
            #if DEBUG
                self._synchronizationTracker.register(synchronizationErrorMessage: .variable)
                defer { self._synchronizationTracker.unregister() }
            #endif
            self._lock.lock()
            self._value = newValue
            self._lock.unlock()

            self._subject.on(.next(newValue))
        }
    }

BehaviorRelay

BehaviorRelay 就是 BehaviorSubject 去掉终止事件 onErroronCompleted

  • 使用案例:
let subject = BehaviorRelay(value: 1)
subject.accept(10)

subject.subscribe({ print("订阅到:\($0)")})
    .disposed(by: disposeBag)

subject.accept(100)
subject.accept(1000)
  • 打印结果:

订阅到:next(10)
订阅到:next(100)
订阅到:next(1000)

  • 分析

    查看源码,请注意 BehaviorRelay 上方的注释,注释中说得非常清楚,BehaviorRelay 是对 BehaviorSubject 的封装,但是和 BehaviorSubject 不一样的地方在于,BehaviorRelay 不会被 errorcompleted 事件终止。

BehaviorRelayVariable 类似, 无需手写 on(event)事件,只需给属性赋值即可拿到响应事件内容,是我们开发中最常用的序列。

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

推荐阅读更多精彩内容