In the previous article, we analyzed the entire subscription process in Rxswift. Before the start of the transformation operation, the first to understand the concept of Chu sink, unclear students can look at an analysis of an article. In a nutshell, a sink convection operation occurs before each subscription operation. If the flow in the RX is treated as water, then the sink is equivalent to the strainer of each water faucet, and the final processing is done before the effluent.
Sink.png
Each time the subscribe, can be likened to water, in every time before the water, Rxswift will do one thing:
Override Func Subscribe<o:observertype> (_ Observer:o), disposablewhere o.e = = Element {if! currentthreadscheduler.isschedulerequired {//The returned disposable needs to release all references once it was disposed.Let disposer = Sinkdisposer () let sinkandsubscription = RUN (Observer, Cancel:disposer) disp Oser.setsinkandsubscription (Sink:sinkAndSubscription.sink, subscription:sinkAndSubscription.subscription)
return Disposer}
else { return CurrentThreadScheduler.instance.schedule (()) {_ in Let disposer = Sinkdi Sposer () let sinkandsubscription = Self.run (Observer, Cancel:disposer) Disposer.setsinkandsubscription (sink: Sinkandsubscription.sink, subscription:sinkAndSubscription.subscription) return disposer} }}
Through the above source code we can find that every time a observable is subscribed, then the observable will certainly execute the run
method, and the run
method of doing is sink related processing operations.
In short, sink mainly do two things:
- Forwarding of Next, complete, error events;
- Prior changes to the flow of the hair.
And our transformation operations are basically operating in a variety of sink, why is that basically? Because in the case of some high-order changes (nested transformations), sink is not the place to change, the specific situation will be mentioned in the following paragraph.
Example
Here is the simplest example code, we start with the most common map
, let's see Krunoslav Zaher
how it is handled map
.
Observable.of(1, 2, 3) $0 * $0 } .subscribe(onNext: { print($0) }) .disposed(by: disposeBag)
We can get map
a breakpoint on top of the method, and after the program runs we can see that the method definition is stopped.
extension ObservableType { public func map<R>(_ transform: @escaping (E) throws -> R) -> Observable<R> { return self.asObservable().composeMap(transform) }}
We can see that there are two things done here, first of all to make sure that the caller Observable
is converted, because the object that conforms to it ObservableType
may be ControlEvent
, ControlProperty
something like that. The method is then called to pass in the closure of the composeMap
transformation operation we expect.
OK, let's go to the next level and see what we've composeMap
done:
@escaping (Element) throws -> R) -> Observable<R> { return _map(source: self, transform: transform) }
We can see that we have Observable
called our own _map
private methods here:
internal func _map<Element, R>(source: Observable<Element>, transform: @escaping (Element) throws -> R) -> Observable<R> { return Map(source: source, transform: transform)}
Final Fileprivate class Map<sourcetype, Resulttype>: producer<resulttype> {Typealias Transform = (SourceTy PE) throws-Resulttype privateLet _source:observable<sourcetype> privateLet _transform:transform init (Source:observable<sourcetype>, Transform: @escaping transform) {_source =SOURCE _transform = transform} override func Composemap<r> (_ Selector: @escaping (resulttype) throws- > R) observable<r> {let originalselector = _transform return Map<SourceType, R> (source: _source, Transform: {(S:sourcetype) throws, R in let r:resulttype = try Originalselector (s) return try Selector (r)})} override Func Run<o:observertype> (_ Observer:o, cancel:cancelable) (Sink:disposable, Subs cription:disposable) where o.e = resulttype {let sink = Mapsink (Transform: _transform, Observer:observer, cancel:cancel) let subscription = _ Source.subscribe (sink) return (Sink:sink, Subscription:subscription)}} /span>
As we can see, the so-called _map
actually returns an object based on the Producer
class (producer inherits from Observable, and the observable class is the place where the first definition composeMap
of the integration chain is important for the next understanding) Map
. Here are three main things to do:
- First, the "observable sequence" and "transform operation" are saved by the constructor.
- Overriding the parent class
composeMap
, from the original direct use of the incoming Transform operation (transform) constructs the map object into a transformation using the Map object's own transform operation, and then using the incoming transform operation to perform a transformation. This recursive processing can achieve the purpose of nested processing map
operations, like this: Observable<Int>.of(1, 3, 5).map(+).map(+)
.
- The method of overriding the parent class,
run
as stated in the previous article, is run
executed before the subscription, and various types of processing are used for the Sink
data source when passing data. And in this case, this is the one that, Sink
MapSink
MapSink
at each next event, uses the incoming transform
data source for processing, and then the processed data source is sent out.
At this point all map
operations have been completed. We can see that map
the operation is actually "inert", that is, when you use the map
action unless you use a nested map or subscribe to the watch sequence, they do not immediately perform the transform operation.
Producer-Consumer model
In the Rxswift design implementation process, in fact, is also the producer-consumer mode (Producer–consumer pattern) practical application. In Rxswift, all observable sequences act as producers, so we can finally return a subclass that inherits from the Producer
class (except that some subject,subject are more special and will be discussed later).
Producer inheritance Overview. png
The brain graph above probably shows the Producer
derived subclasses, and we can see whether the "initialize" method we use often:,, just
of
from
, or the transformation method we commonly use:,, they are map
the flatMap
merge
corresponding implementation is one Producer
.
As we can see, it is also the result of the producer-consumer model that enables Rxswift to 可观察序列
perform custom processing before the end of each pipeline, just like in a factory assembly line.
Work Flow.png Summary
Next we can overlook RxSwift
the operation of the event transformation, the following to do some logical carding work, I hope you can see more clearly:
1. Agreement Development
Start with an agreement. ----WWDC 2015
We know that RxSwift
the observable sequences are based ObservableType
, so when we need to add a transform operation to all the observable sequences, we just need to extension
add a public method and then implement it.
Extension Observabletype {Public Func map<r> (_ Transform:@escaping (E)Throws, R, observable<r> {Return self.asobservable (). Composemap (Transform)}Public Func flatmap<o:observableconvertibletype> (_ Selector: @escaping (E) throws (O) observable<o.e> {return FlatMap (Source:asobservable (), Selector:selector)} public Func concat<o:observableconvertibletype> (_ Second:o), observable<e> where o.e = = E {return observable.concat ([Self.asobservable (), second.asobservable ()])} Public static func Combinelatest<o1:observabletype, o2:observabletype> (_ Source1: O1, _ Source2:o2), observable< (O1. E, O2. E) > {return CombineLatest2 (Source1:source1.asObservable (), Source2: Source2.asobservable (), resultselector: {($0, $1)})} //More ...}
The code I listed above is that I put it in the same place in order to focus on the display extension
, and in the actual source they are scattered in different swift files. So we know that all of the transformations we use are extension
extended to the ObservableType
protocol.
By looking through the source code we can see that the above transformation operation has actually done one thing, that is to return a Producer
specific subclass. For example map
, an instance object of a class is returned Map
, and an combineLatest
instance object of the class is returned CombineLatest2
.
2. The figurative producer
What about the subclasses returned by the extension method? Producer
First, the image Producer
must be rewritten in override func run<O : ObserverType>(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Accumulate
such a way that rxswift the data source through the representational Sink
, and then lets the source observable sequence execute the subscription.
Second, at Producer
least two parameters are received at initialization time: One parameter is the observable sequence being passed, and the other is the closure of the transformation operation. Of course, some transformation operations may require three parameters due to the nature of the operation. For example Scan
, the operation, not only the closure accumulator
, but also the need for one seed
, which is Scan
determined by the characteristics of the operation, here not to repeat. When the Producer
necessary parameters for these transformations are saved, in the run
method sink
it is possible to execute the transformations before the subscription output, and then output them to the subscribers.
It is worth noting that because of run
subscribe
recursive invocation between methods and methods, such implementation patterns also naturally support nested transformation operations.
3. "Toil" Sink
So the execution of the transformed closures is in various categories Sink
, such as MapSink
:
func on (_ event:event<sourcetype>) {switch Event {case. Next (let Element):
do {
///for transform Operation let mappedElement = try _selector (element, try incrementchecked (&_index)) Span class= "Hljs-comment" >///forwards the events after the transform operation to the original observer Forwardon (. Next (Mappedelement))} let e {forwardon (. Error (E)) Dispose ()} case. Error (let error): Forwardon (. Error (Error)) Dispose () case. Completed: Forwardon (. Completed) Dispose ()}}
We can see that here we finally have a transform operation, and after the transformation operation, the result is forwarded to the observer.
At this point, the entire transform chain has been converted.
The regret of the design
On composeMap
top of the definition method, we can see a comment like this:
// this is kind of ugly I know :( // Swift compiler reports "Not supported yet" when trying to override protocol extensions, so ˉ\_(ツ)_/ˉ /// Optimizations for map operator
In the summary of the previous section, we know that the nesting of transform operations in Rxswift is resolved by run
subscribe
recursive invocation of methods and methods. But there are problems here, for example, when you nest 10 map
methods, each occurrence of onnext causes a recursive call of 10 transformations, and then the final value is passed to the subscriber. A simple functional expression is like this:
10(+1)(+1)(+1)(+1)(+1)(+1)(+1)(+1)(+1)(+1) = 20
So why can't we just do that?
10(+10) = 20
Based on this consideration, we can see that map
the default implementation is special, it does not directly return an Map
object, but rather by composeMap
returning an object and Map
then overriding it in the Map
object composeMap
to optimize the function call when a nested call occurs:
class Map<SourceType, ResultType>: Producer<ResultType> { // .... override func run<O: ObserverType>(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == ResultType { let sink = MapSink(transform: _transform, observer: observer, cancel: cancel) let subscription = _source.subscribe(sink) return (sink: sink, subscription: subscription) }}
It is for such an optimization, resulting in the appearance seems ugly
to be, this is also a design regret it.
Maru
Links: https://www.jianshu.com/p/a11234b7a089
Source: Pinterest
Copyright belongs to the author. Commercial reprint please contact the author for authorization, non-commercial reprint please specify the source.
"Taste rxswift Source"-Transform operation (Operators)