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 AnonymousObservable
object , 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 AnyObserver
structure. 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 Observable
is 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 AnonymousObserver
an 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
SinkDisposer
The 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 run
and 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 setSinkAndSubscription
the 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~
AtomicOr
The 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.