sequence


Previous article Mojito: RxSwift for my love, please

There is no mention of DisposeBag recycling

There are two reasons

Because of

Time was too short for me to see it

The second is because

Article too long, will be oral ulcer


Disposable

Don’t say a word. They’re all repeat visitors

Look at the classic case

let obable = Observable<String>.create { observer -> Disposable in
    observer.onNext("dispose")   # step1
    observer.onCompleted()       # step2
    return Disposables.create {
        print("Disposables release") # step6
    }
}

 _ = obable.subscribe(onNext: { (str) in
    print("Sequence" + str) # step3
 }, onError: { _ in
 }, onCompleted: {
    print("Complete callback")   # step4
 }, onDisposed: {
    print("Destroy callback")   # step5}) // Print sequence dispose -> Complete callback -> Destroy callback -> DisposablesCopy the code


Q1: Why is the closure return value of Disposable when the subscription is created?

Let’s get some ideas straight

Disposable

Dispose is an important method called dispose
public protocol Disposable {
    # Deal with recycled resources.
    func dispose()
}
Copy the code


Disposables

# Disposables is a structure
public struct Disposables {
    private init() {}}# Disposables Extend create, return the value AnonymousDisposable
# Destroyer 1
extension Disposables {
    public static func create(with dispose: @escaping () -> Void) -> Cancelable {
        return AnonymousDisposable(disposeAction: dispose)
    }
}
Copy the code

Disposables. Create, you have created the AnonymousDisposable, and pass in closure step6

The return value is of type Cancelable

A protocol named Cancelable that inherits Disposable
public protocol Cancelable : Disposable {
    Has the resource been destroyed?
    var isDisposed: Bool { get }
}
Copy the code


Let’s look at AnonymousDisposable

# Inherit DisposeBase and Cancelable, and have isDisposed & Dispose ()
private final class AnonymousDisposable : DisposeBase, Cancelable {
    # Disposables. Create The closure you created
    public typealias DisposeAction = () -> Void

    private let _isDisposed = AtomicInt(0)
    private var _disposeAction: DisposeAction?
    Is it destroyed
    public var isDisposed: Bool {
        return isFlagSet(self._isDisposed, 1)
    }
    Save the closure
    private init(_ disposeAction: @escaping DisposeAction) {
        self._disposeAction = disposeAction
        super.init()
    }
    Core methods are private for your own use only
    fileprivate func dispose() {
        if fetchOr(self._isDisposed, 1) == 0 {
            if let action = self._disposeAction {
                self._disposeAction = nil
                action()
            }
        }
    }
}
Copy the code

AtomicInt(0) : inherits NSLock and assigns the original value to 0

FetchOr and isFlagSet are methods of the AtomicInt class and are thread-safe


IsFlagSet:

func isFlagSet(_ this: AtomicInt, _ mask: Int32) -> Bool {
    return (load(this) & mask) != 0
}
When self._isDisposed is not 0, they are disposed
# isFlagSet returns true, isFlagSet isDisposed to true
Copy the code


FetchOr:

func fetchOr(_ this: AtomicInt, _ mask: Int32) -> Int32 {
    this.lock()
    let oldValue = this.value
    this.value |= mask
    this.unlock()
    return oldValue
}
# | for operation, only when the zero for the first time, return oldValue 0
# External equation holds
# newValue is the value 1 after the bit operation, so the next equation will not equal 0
# Purpose: Destroy only once
Copy the code


dispose()

if let action = self._disposeAction {
    self._disposeAction = nil
    action()
}
The temporary action variable holds the external closure
Set external closure to nil
Execute closure
Copy the code


So here’s the summary

  • summary
    • Disposables.create_subscribeHandlerWhen the closure executes the callback
    • When you perform_subscribeHandlerAn anonymous destroyer must be createdAnonymousDisposable
    • AnonymousDisposableInstance, holding outsideDestruction of closure
    • AnonymousDisposable, has theThe ability to destroy and only destroy once, as well asWhether to destroyThe judgment of the


The destruction chain is as follows:


Disposables

This raises a second question

Q2: When is Dispose called?


Time goes back to subscribe

 public func subscribe(onNext: ((Element) -> Void)? = nil, onError: ((Swift.Error) -> Void)? = nil, onCompleted: (() -> Void)? = nil, onDisposed: (() -> Void)? = nil)
    -> Disposable {
        let disposable: Disposable
        # Prompt the world into an onDisposed closure, print(" Dispose callback ")
        # Create AnonymousDisposable instance
        if let disposed = onDisposed {
            disposable = Disposables.create(with: disposed)
        } else {
            disposable = Disposables.create()
        }
        
        let observer = AnonymousObserver<Element> { event inswitch event { ... omitcase .error(let error):
                disposable.dispose()
            case .completed:
                disposable.dispose()
            }
        }
        # Return to the Disposables. Create
        return Disposables.create(
            self.asObservable().subscribe(observer),
            disposable # Destructor 2 onDisposed)}Copy the code


So you can see that when the world calls subscribe, they pass in the onDisposed closure

The return value of SUBSCRIBE is also of type Disposable, which is also returned through Disposables. Create

You have a look at

This Disposables. Create, it has two parameters


Something strange happened

Point in

extension Disposables {
    Create a new destroyer from 2 destroyers
    public static func create(_ disposable1: Disposable, _ disposable2: Disposable) -> Cancelable {
        return BinaryDisposable(disposable1, disposable2)
    }
}
Copy the code


Not for the sake of a father, but your family grave might be gone today

Said Disposables. Creat Disposables


BinaryDisposable destroyers, just like AnonymousDisposable, so I’m not going to do it here

private final class BinaryDisposable : DisposeBase, Cancelable {
    .... 
    # add one more parameter inside
    var isDisposed: Bool {
        return isFlagSet(self._isDisposed, 1)
    }
    
    func dispose() {
        iffetchOr(self._isDisposed, 1) == 0 { self._disposable1? .dispose() self._disposable2? .dispose() self._disposable1 = nil self._disposable2 = nil } } }Copy the code


The problem comes to the binary destructor’s first argument self.asObservable().subscribe(observer)

It can be found in Producer

override func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
        # Pipe cleaner
        # Return to a Disposer instance, and as it dissolves, release all references in the pipeline
        let disposer = SinkDisposer()
        
        Pass Disposer and subscriber to the run sequence AnonymousObservable
        let sinkAndSubscription = self.run(observer, cancel: disposer)
        disposer.setSinkAndSubscription(sink: sinkAndSubscription.sink, subscription: sinkAndSubscription.subscription)
        return disposer
}
Copy the code

A pipeline cleaner SinkDisposer instance Disposer is created to hold the generated sink and subscription separately by calling run of AnonymousObservable

And return the disposer


In the AnonymousObservable

final private class AnonymousObservable<Element>: Producer<Element> {
    override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {
        Generate an anonymous watch channel through the destroyers passed in above, and subscribers
        let sink = AnonymousObservableSink(observer: observer, cancel: cancel)
        The run call, which executes the _subscribeHandler closure, assigns the returned value to the subscription
        # Subscription refers to destroyer 1
        let subscription = sink.run(self)
        return (sink: sink, subscription: subscription)
    }
}
Copy the code

AnonymousObservableSink inherits from Sink, which also inherits from Disposable, representing that AnonymousObservableSink also has the ability of Dispose ()


Teacher, I feel dizzy

Can I have the DisposeBag first

I spit


Q3: What is the relationship between AnonymousObservableSink and SinkDisposer?

A 3: SinkDisposer will pass its instance to the channel AnonymousObservableSink by anonymous observation sequence, and channel Sink has the final interpretation right


It means that when the outside world executes its subscribe, it will creat a Disposables

Disposables contains:

  • OnDisposed disposer 2
  • Plumbing cleaner SinkDisposer
    • sink
      • AnonymousObservableSink
    • subscription
      • Destruction of 1





Because of the addition of the concept of Sink, Sink, as a translator, handles the relationship between the sequence and subscribers and destroyers

If you compare Sink to a building

SinkDisposer is the cleaner of the building, SinkDisposer is responsible to dispose of all disposables of the building, but the power of dispatching and interpretation belongs to Sink


SinkDisposer

private final class SinkDisposer: Cancelable {
    private enum DisposeState: Int32 {
        case disposed = 1
        case sinkAndSubscriptionSet = 2
    }
    The initial value is 0
    private let _state = AtomicInt(0)
    private var _sink: Disposable?
    private var _subscription: Disposable?
    Set setSinkAndSubscription
    func setSinkAndSubscription(sink: Disposable, subscription: Disposable) {
        self._sink = sink
        self._subscription = subscription
        
        # 0 | 2, for the first time to return to oldValue 0, previousState is 0
        # _state is assigned to 2
        let previousState = fetchOr(self._state, DisposeState.sinkAndSubscriptionSet.rawValue)
        
        # first previousState = 0, 0 & 2 = 0, continue
        if(previousState & DisposeState.sinkAndSubscriptionSet.rawValue) ! = 0 { rxFatalError("Sink and subscription were already set")}# 0 & 1 = 0, not if
        if(previousState & DisposeState.disposed.rawValue) ! = 0 { sink.dispose() subscription.dispose() self._sink = nil self._subscription = nil } }The above method has already been used once
    func dispose() {
        # after the first call setSinkAndSubscription _state to 2, 2 | 1 = 3, fetchOr returns the old value 2, _state into 3
        # previousState is 2
        let previousState = fetchOr(self._state, DisposeState.disposed.rawValue)
        
        # 2&1 = 0, do not enter if, continue walking
        if(previousState & DisposeState.disposed.rawValue) ! = 0 {return
        }

        # 2&2 = 2, dispose, if is satisfied, respectively
        if(previousState & DisposeState.sinkAndSubscriptionSet.rawValue) ! = 0 {Dispose is called # AnonymousObservableSink
            sink.dispose() 
            Dispose is called
            subscription.dispose()
            After destruction, set nil respectively
            self._sink = nil
            self._subscription = nil
        }
    }
}
Copy the code


Q A

Q1: Why is the closure return value of Disposable when the subscription is created?


The Disposable type refers to the anonymous destructor, which is destructor 1. Because you want to dig a grave and erect a tombstone for the sequence when it’s created

As wild said

I bloom in slaughter like a flower in the dawn

Dying doesn’t matter. Being handsome is done


Q2: When is Dispose called?


In this article, onCompleted is explicitly called, which executes the SUBSCRIBE onCompleted closure and then calls

disposable.dispose()

case .completed:
    onCompleted?()
    disposable.dispose() # Destroyer 2
}
Copy the code

The print order is complete callback -> Destroy callback

The onCompleted signal must flow down the pipe to AnonymousObservableSink, calling sink’s ON, i.e

case .error, .completed:
    if fetchOr(self._isStopped, 1) == 0 {
        self.forwardOn(event)
        self.dispose() Dispose of the parent class Sink, which calls SinkDisposer}}Copy the code

Then SinkDisposer will take care of Sink and disposer 1

So finally print Disposables 1’s closure -> Disposables


Execution order details

Here we derive Q 4: What if onCompleted is not explicitly called? Instead of

let obable = Observable<String>.create { observer -> Disposable in
    observer.onNext("dispose")
    // observer.onCompleted()
    return Disposables.create {
         print("Disposables release")}}let dispose  = obable.subscribe(onNext: { (str) in
    print("Sequence" + str)
}, onError: { _ in
    print(Error callback)
}, onCompleted: {
    print("Complete callback")
}, onDisposed: {
    print("Destroy callback")})Dispose is explicitly called
dispose.dispose()
Copy the code

And then you’re going to call SinkDisposer’s dispose, primary BinaryDisposable you remember, SinkDisposer is going to disposer first, and disposer 2 onDisposed

So print the sequence dispose -> Disposables release -> Destroy callback


Q 4, Q 5 can be far behind?

Q 5: Why does Dispose dispose dispose the response?

Because we destroyed the Sink, the building, the communication pipeline and the address of the delivery boy

How do you respond?

Of course, sequences and observers, after all, are mundane instance objects in iOS that will be destroyed over the lifetime of the controller


I heard that

Deduct 1 if you understand

Do not understand the buckle foot


DisposeBag

RxSwift also has a garbage collection method, DisposeBag can be called garbage bag

Think of it as an autoReleasePool

It’s full of destroyers

As you throw the DisposeBag into the trash, the controller’s life cycle ends

The destroyers inside will be destroyed one by one


rx.disposeBag

Because NSObject+Rx is an elegant extension of garbage bags, we don’t need to create disposebags ourselves

 viewModel.title.asDriver()
 .drive(titleLabel.rx.text)
 .disposed(by: rx.disposeBag)

Just call rx.disposeba
# pod 'NSObject + Rx', '~ > 5.0 # https://github.com/RxSwiftCommunity/NSObject-Rx
Copy the code

If you are interested, take a look at the source code for Rx. disposeBag


After the

Above is my understanding of DisposeBag, please do not hesitate to correct

Hopefully after you finish these two mojito flavored RxSwift

No more trash bags