RXSwift source code analysis (1)

Briefly

Recently, the boss gave a new project, and I plan to write it in Swift. The original RAC used for OC, and I wanted to try the new Swift natural framework, so I used RXSwift. I can use both of these frameworks, but I don't understand the principle, just as the demand has not come down recently, I studied and studied RXSwif, and shared my gains. If there are any inaccuracies in the text, I hope you can correct me~

I will not talk nonsense about what RXSwift is and how to use it. There are many online resources. This article starts with the implementation principle of ObservableSingle .

Use Demo

The following is a simple code to use Observable

        let numbers: Observable<Int> = Observable.create { observer -> Disposable in
            observer.onNext(0)
            observer.onNext(1)
            observer.onCompleted()
            
            return Disposables.create {
            }
        }

        numbers.subscribe{
            print($0) } 

The effect achieved by the demo is actually to extract the generated events (0, 1, Completed) entered in the previous closure, and extract them from the next closure. This separates the generation of events and the processing of events. This article is to analyze this how the effect is achieved

main class

AnonymousObservable

Anonymous observables that store closures that generate events and activate and process event closures

AnyObserver

Arbitrary observers for storing events and outputting events

AnonymousObserver

Anonymous observer, used to store closures that handle events

AnonymousObservableSink

Link observables and observers to achieve event delivery

ObserverType, ObservableType.. Protocol

Agreement, wrapping all the above contents and restricting them to facilitate effective communication~

Event

The event itself is an enumeration with Error, Complete, Element (element)

Implementation process

storage

The first thing to say is some of the content defined by ObserverType

associatedtype E

func subscribe<O: ObserverType>(_ observer: O) -> Disposable where O.E == E

E: Define a definite type for this event stream to ensure that the generated and processed elements are of the same type, otherwise it cannot be delivered

create method

Observable<Int>.create { observer -> Disposable in ....}For Observable, it is an abstract class, we can't use it in actual use, there is a default implementation in the protocol

extension ObservableType {
    public static func create(_ subscribe: @escaping (AnyObserver<E>) -> Disposable) -> Observable<E> {
        return AnonymousObservable(subscribe)
    }
}

So what is created here is the AnonymousObservableobject , I will call it A1 first , A1 holds the closure generated by the event, and the event generated in the closure is input into the AnyObserverstructure. The closure will become A2 so that the storage part is good~~

activation

To activate, we call the subscription method of A1subscribe (also a method defined in the protocol), and then look at the implementation in the method~ Because it Observableis an abstract class, this is also the default implementation of the protocol

    public func subscribe(_ on: @escaping (Event<E>) -> Void)
        -> Disposable {
            let observer = AnonymousObserver { e in
                on(e)       
            }

            return self.asObservable().subscribe(observer)
    }

There are two steps here, one is the implementation of the observer, but the transmission of the event

observer

It is very simple here, that is, to create AnonymousObserveran anonymous observer object B1 , B1 holds the event processing closure, and the closure we become B2

transfer

The first is the asObservable()method, because B1 is indirectly inherited from Observable, so that is return self, it should be used when dealing with other types of observables, and I will add it later if I encounter it~

Then there is another subscription method (overloading) for A1 , passing B1 as a parameter to the details, let's not talk about the details, first grasp the backbone~

    override func subscribe<O : ObserverType>(_ observer: O) -> Disposable where O.E == Element {
        
        if !CurrentThreadScheduler.isScheduleRequired {
            //第一步
            let disposer = SinkDisposer()
            //第二步
            let sinkAndSubscription = run(observer, cancel: disposer)
            //第三步
            disposer.setSinkAndSubscription(sink: sinkAndSubscription.sink, subscription: sinkAndSubscription.subscription)
            return disposer } //else先不说~ else { return CurrentThreadScheduler.instance.schedule(()) { _ in let disposer = SinkDisposer() let sinkAndSubscription = self.run(observer, cancel: disposer) disposer.setSinkAndSubscription(sink: sinkAndSubscription.sink, subscription: sinkAndSubscription.subscription) return disposer } } } 

first step

SinkDisposerThe object is about the object that handles resource recovery after the transfer is over, call it C1 , which is used to handle the disposer closure returned by the A1 create closure~

second step

The method is called runand the B1 object is passed in

    override func run<O : ObserverType>(_ observer: O, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where O.E == Element {
        //2.1  
        let sink = AnonymousObservableSink(observer: observer, cancel: cancel)
        //2.2
        let subscription = sink.run(self)
        //2.3
        return (sink: sink, subscription: subscription)
    }

2.1 step

Create AnonymousObservableSink object, I call it D1 , it also holds B1 object and C1 object

2.2 step

Call the method of the D1 object and pass in A1 itselfrun

 func run(_ parent: Parent) -> Disposable {
        return parent._subscribeHandler(AnyObserver(self))
    }

In this method, the A2 closure of the A1 object is called, and the D1 object is converted into a structure and passed in as the A2 parameter~AnyObserver

Then we see how the D1 object is converted

    //结构体方法
    public init<O : ObserverType>(_ observer: O) where O.E == Element {
        self.observer = observer.on
    }

Here the structure holds the on method of the B1 object held by D1 as an attribute~, and the structure becomes E1

Let's look at the method of E1 againonNext....

extension ObserverType {
    //YSD
    /// Convenience method equivalent to `on(.next(element: E))`
    ///
    /// - parameter element: Next element to send to observer(s)
    public func onNext(_ element: E) {
        on(.next(element))
    }
    
    /// Convenience method equivalent to `on(.completed)`
    public func onCompleted() {
        on(.completed)
    }
    
    /// Convenience method equivalent to `on(.error(Swift.Error))`
    /// - parameter error: Swift.Error to send to observer(s)
    public func onError(_ error: Swift.Error) {
        on(.error(error))
    }
}

The corresponding method is actually to call B1on ~~

    func on(_ event: Event<E>) {
        switch event {                      
        case .next:
            if _isStopped == 0 {            
                onCore(event)
            }
        case .error, .completed:

            if AtomicCompareAndSwap(0, 1, &_isStopped) {
                onCore(event)
            }
        }
    }

Corresponding method of B1onCore

    override func onCore(_ event: Event<Element>) {
        return _eventHandler(event)         
    }

That is , the event received by E1 from A2 is passed into B2 , and the content is finally delivered~~ and then the closure that releases resources in A1 is returned~

2.3

Return D1 and the disposable closure as a tuple~

third step

C1 receives the tuple parameter, calls setSinkAndSubscriptionthe method~, and then returns the SinkDisposer object, allowing the user to choose whether to release~

icon

The text is too abstract, please draw a picture~ The drawing is a bit ugly (๑•ᴗ•๑)~

 

 

It can be seen that A1 only holds A2 during this process, which will not cause memory leaks~ Of course, if you use dispose improperly, there must be leaks~ Pro test (๑•ᴗ•๑)~

minutiae

1

subscribe in 2if !CurrentThreadScheduler.isScheduleRequired

The content is like this~

    public static fileprivate(set) var isScheduleRequired: Bool {
        get {     
            //获取该指示值
            return pthread_getspecific(CurrentThreadScheduler.isScheduleRequiredKey) == nil
        }
        set(isScheduleRequired) {
            
            // 成功返回0            true设置no no设置为 true if pthread_setspecific(CurrentThreadScheduler.isScheduleRequiredKey, isScheduleRequired ? nil : scheduleInProgressSentinel) != 0 { rxFatalError("pthread_setspecific failed") } } } private static var isScheduleRequiredKey: pthread_key_t = { () -> pthread_key_t in //YSD //https://onevcat.com/2015/01/swift-pointer/ //可变指针 pthread_key_t类型 分配空间 let key = UnsafeMutablePointer<pthread_key_t>.allocate(capacity: 1) defer { key.deallocate(capacity: 1) } //创建线程安全的变量 guard pthread_key_create(key, nil) == 0 else { rxFatalError("isScheduleRequired key creation failed") } return key.pointee }() 

This should be to protect the data security of RXSwift under multi-threaded operation~ In this event flow, only the get method is used, and set is not used, so I don't know the specific effect~, I will add it later when I encounter it. ~

SinkDisposer

is to release the resource part~

    fileprivate enum DisposeState: UInt32 {     
        case disposed = 1
        case sinkAndSubscriptionSet = 2
    }

    // Jeej, swift API consistency rules    
    fileprivate enum DisposeStateInt32: Int32 {
        case disposed = 1
        case sinkAndSubscriptionSet = 2
    }
    
    private var _state: AtomicInt = 0
    private var _sink: Disposable? = nil
    private var _subscription: Disposable? = nil
    
    
 func setSinkAndSubscription(sink: Disposable, subscription: Disposable) { _sink = sink _subscription = subscription let previousState = AtomicOr(DisposeState.sinkAndSubscriptionSet.rawValue, &_state) if (previousState & DisposeStateInt32.sinkAndSubscriptionSet.rawValue) != 0 { rxFatalError("Sink and subscription were already set") } if (previousState & DisposeStateInt32.disposed.rawValue) != 0 { sink.dispose() subscription.dispose() _sink = nil _subscription = nil } } func dispose() { let previousState = AtomicOr(DisposeState.disposed.rawValue, &_state) if (previousState & DisposeStateInt32.disposed.rawValue) != 0 { return } if (previousState & DisposeStateInt32.sinkAndSubscriptionSet.rawValue) != 0 { guard let sink = _sink else { rxFatalError("Sink not set") } guard let subscription = _subscription else { rxFatalError("Subscription not set") } sink.dispose() subscription.dispose() _sink = nil _subscription = nil } } 

You can know from the output of the crash prompt~ This is to prevent multiple calls of dispose~ Because in the entire event flow, the dispose closure may generate Complete, Error or manually called by the user~

AtomicOrThe method actually calls is OSAtomicOr32OrigBarrier(A,&B)that the function will return the result of the thread-safe bitwise OR operation of the two variables, and assign the value to the latter = former ~ B=A

When the dispose is not called, the logical AND operation state = 2 previousState = 0 neither of the two conditions is established~ So at this time, the user has to manually dispose

It has been called before, that is, complete or Error occurs (there is also a guarantee in the above code, the two only happen together~), then state = 1 When the setSinkAndSubscription method is called, the logical AND operation state = 2 previousState = 1, then the first condition does not hold The second set up ~ release resources

When Complete multiple times, it will only be dispose once~

When called multiple times in the outside world, state = 2 previousState = 1, then the first condition is established and crashes~

Of course, there are many ways to achieve this effect here~ RSSwift's solution is more compelling~

Summarize

After reading these source codes, I feel that RXSwift implements the design pattern very thoroughly~ When the time is rich, the projects written by myself should move closer to him to enhance the ductility of the project, so that the project manager will not have too much headache to add anything. ~~


Author: dearmiku
Link: https://juejin.im/post/5a355ab15188252bca04f0fd
Source: Nuggets The
copyright belongs to the author. For commercial reprints, please contact the author for authorization, and for non-commercial reprints, please indicate the source.
 
https://juejin.im/post/5a355ab15188252bca04f0fd
https://juejin.im/post/5a38d34ff265da430d582355

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325984974&siteId=291194637