In general, an observable sequence emits an error or completed event, and all internal resources are freed. But if you need to release these resources early or unsubscribe, you can call the Dispose method on the Disposable returned. After calling the Dispose method, the subscription will be cancelled and the internal resources will be freed.
Here’s an example:
// Step 1: Create sequence
let ob = Observable<String>.create { (observer) -> Disposable in
// Step 3: Send a signal
observer.onNext("Information 1")
return Disposables.create{ print("Destroyed and released.")}}// Step 2: Subscribe signal
let disposable = ob.subscribe(onNext: { (text) in
print("Subscription information:\(text)")
}, onError: { (error) in
print("error: \(error)")
}, onCompleted: {
print("End of subscription") {})print("Destroyed")}// Unsubscribe/release
disposable.dispose()
Copy the code
Result: Subscription information: information1Destroy released destroyedCopy the code
Based on the above example, examine the underlying logic.
subscribe
Let’s look at the SUBSCRIBE source again.
Introduction to RxSwift core logic
self.asObservable().subscribe(observer)
dispose
Disposables.create
Disposables.create
BinaryDisposable
BinaryDisposable
Disposable
-
Disposable = Disposables. Create (with: Disposed).
By analyzing the source code, it is clear that
disposable
Is one that holds the destruction closureAnonymousDisposable
Anonymous destroyers. -
Argument 1self.asObservable().subscribe(observer) : as analyzed in the introduction to RxSwift core logic, this code eventually calls producer.subscribe
So it’s going to return one
SinkDisposer
Instance object of.SinkDisposer
You also need two parameters, and these two parameters come fromlet sinkAndSubscription = self.run(observer, cancel: disposer)
. So let’s go furtherrun
Function.run
The tuple function returns a tuple whose first value issink
The second issink.run(self)
Returns the result according toIntroduction to RxSwift core logicFrom the analysis ofsink.run(self)
The result is the closure return value that created the sequenceDisposables. Create {print(" Destroy release ")}
.
At this point, the analysis of the two parameters is finished. Then let’s analyze what the process of Disposable.Dispose () does?
Before analyzing the process, let’s insert a little bit of knowledge. FetchOr = fetchOr; fetchOr = fetchOr; fetchOr = fetchOr; fetchOr = fetchOr; Let’s analyze it first.
func fetchOr(_ this: AtomicInt, _ mask: Int32) -> Int32 {
this.lock()
let oldValue = this.value
this.value |= mask
this.unlock()
return oldValue
}
Copy the code
This is the source of the fetchOr function. The fetchOr function can be simplified to:
func fetchOr(_ this: AtomicInt, _ mask: Int32) -> Int32 {
let oldValue = this.value
this.value |= mask
return oldValue
}
Copy the code
This is the passed AtomicInt value with only one value inside. FetchOr returns a copy of this.value as the result. And enclosing the value and the mask do or (|) operation. And assigns the result of the or operation to this.value. Maybe it’s not very clear, but let’s use a diagram to visualize it.
fetchOr
disposable.dispose()
As can be seen from the above analysis, dispose() in BinaryDisposable is called when it is executed in the example.
func dispose(a) {
if fetchOr(self._isDisposed, 1) = =0 {
self._disposable1? .dispose()self._disposable2? .dispose()self._disposable1 = nil
self._disposable2 = nil}}Copy the code
There’s a fetchOr function. If self._isDisposed is AtomicInt(0), this.value = 0, mask = 1, then the final return value is 0, and self._isDisposed value becomes 1. That is, the fetchOr function returns 0 only on the first execution and 1 thereafter. It also means that Dispose () is executed only once.
Dispose () calls both parameters passed in at initialization. When the call is done, set it to nil, and the subscription is destroyed. So, let’s take a closer look at the destruction logic in the order we call it.
-
self._disposable1? .Dispose () When it calls Dispose () on _Disposable1, it actually calls SinkDisposer’s dispose function.
Eventually it will be called
sink.dispose()
和subscription.dispose()
And will beself._sink
和self._subscription
Set to nil.-
Sink.dispose () then calls AnonymousObservableSink’s dispose(). According to source code analysis, Dispose () will execute cancel passed in AnonymousObservableSink initialization, Cancel is also the SinkDisposer instance object initialized in producer. subscribe. That is, it will return to SinkDisposer’s dispose function, which will execute only once because of the fetchOr function. So sink.dispose() is equivalent to idling around without executing anything.
-
Dispose (), which holds the return value of the closure that created the sequence, Disposables. Create {print(” Dispose “)}, It’s AnonymousDisposable. AnonymousDisposable call dispose().
The saved closure is eventually executed and printed out
Destroy and release
. And sets the saved closure to nil. -
self._subscription = nil
Release the saved closure.
-
self._sink = nil
Setting self._sink to nil is the key to the entire destruction process. Through the previous analysis [Introduction to RxSwift Core Logic], the importance of sink has been clarified, which plays an important role of connecting observable sequence, observer and scheduler. Now it is associated with destroyer. It can be said that without sink, all processes cannot flow normally. So setting self._sink to nil breaks the connection between the observable sequence and the observer and frees up any intermediate temporary objects.
To complete the self. _disposable1? .Dispose () call.
-
-
self._disposable2? .Dispose () also analyzed in the last section, _Disposable2 is the AnonymousDisposable parameter that saves the onDisposed closure parameter of the subscribe function when it is disposed. When dispose is called, it also calls back the closure print(” destroyed “), so it will print destroyed.
At this point, the underlying analysis of Disposable purgable resources is complete.
Summary: After the above source code analysis of Disposable, it can be known that there are three core processes of Disposable:
- Destroys the AnonymousObservableSink object that connects the observable sequence to the observer, breaks the connection, and unsubscribes. This is the core logic
- Returned when the callback created the sequence
Disposables. Create {print(" Destroy release ")}
Closure in.- To call back a subscription signal
onDisposed
Closure.
Practical use of DisposableDisposeBag
In normal cases, we do not need to call the Dispose function manually, the above example is just for the sake of analyzing the underlying logic. In practice, we typically use DisposeBag to automatically manage the subscription lifecycle.
Example:
let subject = PublishSubject<String>()
subject.onNext("🐘")
subject.subscribe(onNext: { print("Subscribed to:\ [$0)") })
.disposed(by: disposeBag)
Copy the code
Where: disposeBag isControllerProperties held. whenControllerWhen destroyed, all subscriptions are destroyed.Copy the code
Let’s examine the underlying implementation of DisposeBag.
-
dipsosed()
dipsosed()
Is called theDisposeBag.insert
Method to add the destroyer tobag
In the.View the source code for DisposeBag.
insert
Function is executed_insert
Function and call on its return valuedispose
Methods. becauseself._isDisposed
Value is false,_insert
Function returns nil. Also add the destroyer toself._disposables
In an array. -
With the Use of the DisposeBag, we no longer need to manually manage subscription destruction, so it must be the DisposeBag that automatically handles the destruction for us. So when does it do the destruction? DisposeBag can automatically manage destruction, which can only be implemented automatically in its life cycle functions. We continue to look at the source code.
We can do it at
DisposeBag
In the source code, clearly seedeinit
Methods. It calls its owndispose
Methods.Dispose function obtains all the saved destroyers through self._dispose() and performs the dispose dispose operation on each of them. The _dispose function copies an array of destroyers and returns it. Remove all elements that have been saved from self. _Disposables. This is done in order to isolate the source data so that the internal destruction will not be affected by external environmental changes when the destruction operation is performed on the destructor.
The above is the source code analysis of DisposeBag. Its core principle is to call Dispose method on all internally saved destroyers when deinit method is executed at the end of the life cycle to complete the operation of destroying subscriptions.
Please comment on the underlying analysis of Disposable in the above RxSwift core if there is any deficiency.