"Taste rxswift Source"-Transform operation (Operators)

Source: Internet
Author: User
Tags closure

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:

    1. Forwarding of Next, complete, error events;
    2. 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&gt, 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:

    1. First, the "observable sequence" and "transform operation" are saved by the constructor.
    2. 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(+) .
    3. 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)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.