The last article introduced you to the basic concepts and usage of Observables. But in most cases, we need to add data to an Observable and send it to subscribers at runtime. In this requirement scenario, we have to use another type of object in RxSwift – Subject.
In the application, the Subject actually plays two different roles simultaneously: an observable and an observer. This means that the Subject instance object can both receive and send events. For example, the Subject instance object could receive the next event information and then send it to its own subscribers. Sample code:
let subject = PublishSubject<String> ()let subscriptionOne = subject
.subscribe(onNext: { string in
print(string)
})
subject.on(.next("1"))
/* Print the result: 1 */Copy the code
The above code uses an example of the PublishSubject type, and there are four types of Subject in RxSwift:
- PublishSubject: Contains no data when initialized and only sends subsequent data to the subscriber.
- BehaviorSubject: A BehaviorSubject creates a BehaviorSubject that includes initial data and sends subsequent data and the latest data to subscribers.
- ReplaySubject: The object cache size needs to be specified at creation time, which represents the size of the pre-subscription data to be re-sent to the subscriber.
- Variable: BehaviorSubject Indicates the encapsulation type of the object. It saves the current data as state and resends only the most recent or initial value to the subscriber.
The following details the concepts of these four types of objects and their differences and uses.
PublishSubject
If you only want the subscriber to access the data generated during the lifetime of the observed, then you can use PublishSubject entirely. And the PublishSubject object behaves as expected, sending only data to the subscriber after the subscription has started.
For example, the top timeline in the figure below represents events sent by observers, while the bottom two represent different observers. As you can see below, both observers will only receive events sent after the subscription and will not know what happened before.
The corresponding code is:
let subject = PublishSubject<String> ()let subscriptionOne = subject
.subscribe(onNext: { event in
print("1) \( event.element ?? event)" )
})
subject.on(.next("1"))
let subscriptionTwo = subject
.subscribe(onNext: { event in
print("2) \(event.element ?? event)")
})
subject.on(.next("2"))
subject.on(.next("3"))
1) 1) 2) 2) 1) 3) 3 */Copy the code
If we unsubscribe from subscriptionOne at this point and send new data, the result would be:
subscriptionOne.dispose()
subject.on(.next("4"))
/* Print the result 2) 4 */Copy the code
In addition, when the PublishSubject object’s life cycle ends, the object simply sends the event that ended the life cycle earlier, regardless of whether there is more data to produce.
// End the life cycle
subject.onCompleted()
// Send new data
subject.onNext("5")
// End observation
subscriptionTwo.dispose()
let disposeBag = DisposeBag(a)// Re-subscribe
subject
.subscribe {
print("3)", $0.element ?? $0)
}
.addDisposableTo(disposeBag)
// Send new data
subject.onNext("?")
/* Prints the result 2) completed 3) completed */Copy the code
PublishSubject is obviously a good choice for timing sensitive operations. But not all cases are time-sensitive, and sometimes we might want to know the last time we subscribed. At this point, we need to use a BehaviorSubject object.
BehaviorSubject
The BehaviorSubject behaves almost the same as the PublishSubject, but the BehaviorSubject sends one more recent data to the subscriber. The diagram is as follows:
The top of the diagram corresponds to the transmitted data, with the second row representing the first observer and the third row representing the other. It can be found that the first observer observed after 1 and before 2, but it can still get data 1. We can verify this with code:
let subject = BehaviorSubject(value: "1")
let bag = DisposeBag()
subject
.subscribe { event in
print("1) event: \(event.element!) ")
}
.addDisposableTo(bag)
subject
.subscribe { event in
print("2) event: \(event.element!) ")
}
.addDisposableTo(bag)
subject.onNext("2")
subject
.subscribe { event in
print("3) event: \(event.element!) ")
}
.addDisposableTo(bag)
subject.onNext("3")Copy the code
Because the latest data is always available, BehaviorSubject is a good choice for scenarios that might need default values.
ReplaySubject
The behavior of the ReplaySubject is very similar to that of the BehaviorSubject, except that the ReplaySubject allows the subscriber to get more than one recent data. So in a sense, the latter is a special case of the former.
Here is a ReplaySubject object with buffer size 2. It sends data three times, and the first observer gets all the data. The second observer, although started after the second data transmission, can still retrieve the data stored in the cache.
The code is shown as follows:
let subject = ReplaySubject<String>.create(bufferSize: 2)
let bag = DisposeBag()
subject
.subscribe { event in
print("1) event: \(event.element!) ")
}
.addDisposableTo(bag)
subject.onNext("1")
subject.onNext("2")
subject
.subscribe { event in
print("2) event: \(event.element!) ")
}
.addDisposableTo(bag)
subject.onNext("3")
/* Prints the result: 1) event: 1 1) Event: 2 2) Event: 1 2) Event: 2 1) Event: 3 2) Event: 3 */Copy the code
It is worth noting, however, that because the ReplaySubject cache mechanism uses an array structure, a large number of ReplaySubject objects can cause a memory explosion. In addition, memory problems can occur if the cache objects are memory consuming resources such as images. Therefore, ReplaySubject cannot be abused and the cache size should be set properly.
Variable
As mentioned earlier, the Variable type is a wrapper type of BehaviorSubject, so in a sense you could subclass the BehaviorSubject (it’s not). The behavior of an instance of a Variable type is the same as that of a BehaviorSubject, except that the BehaviorSubject adds some of its own properties. You can use the value attribute to access and set the current state of the Variable instance, which means you don’t need to call onNext(_:).
As a encapsulated type of BehaviorSubject, Variable also needs to set a default value during initialization. Also, its behavior of sending data is consistent with that of the BehaviorSubject: it only re-sends the latest or initial value to the subscriber. Another unique feature is that Variable instances do not raise error events. That is, you can subscribe to error events for Variable instances, but you cannot add an error event to the instance.
Code examples:
var variable = Variable("Initial value")
let bag = DisposeBag()
variable.value = "New initial value"
variable.asObservable()
.subscribe { event in
print("1) event: \(event.element!) ")
}
.addDisposableTo(bag)Copy the code
The original address