1. 程式人生 > >RxSwift入門教程(一)

RxSwift入門教程(一)

Getting Started(This project tries to be consistent with ReactiveX.io.The general cross platform documentation and tutorials should also be valid in case of Rxswift.)
*Observables aka Sequences
*Disposing
*Implicit observable guarantees
*Creating your first Observable(aka observable sequence)

Observables aka Sequences(Basics-The equivalence of observer pattern(Observablesequence)and normal sequences(Sequence)is the most important thing to understand about Rx.Every observable sequence is just a sequence.The key advantage for an observable vs swift’s sequence is that it can also receive elements asynchronousy.This is the kernel of the RxSwift,documentation from here is about ways that we expand on that idea.)
.Observable(ObservableType)is equivalent to sequence
.Observable.subscribe method is equivalent to sequence.makeIterator method.
.Observer(callback)needs to be passed to observable.subscribe method to receive sequence elements instead of calling next() on the returned iterator.
Sequences are a simple,familiar concept that is easy to visualize.
People are creatures with huge visual corexes.When we can visualize a concept easily,it’s a lot easier to reason about it.We can lift a lot of the cognitive load from trying to simulate event state machines inside every Rx operator onto high level operations over sequences.
If we don’t use Rx but model asynchronous systems,that probably means our code is full of state machines and transient states that we need to simulate instead of abstracting away.
List and sequences are probably one of the first concepts machematicians and programmers learn.
Here is a sequence of numbers:
–1–2–3–4–5–6–|//terminates normally
Another sequence,with characters:
–a–b–a-a–a–d–x//terminates with error
some sequences are finite while others are infinite,like a sequence of button taps:
–tap-tap—–tap——->
These are called marble diagrams.There are more marble diagrams are rxmarbles.com.
If we were to specify sequence grammar as a regular expression it would look like:
next(error | completed)?*
This describes the following:
.Sequences can have 0 or more elements.
.Once an error or completed event is received,the sequence cannot produce any other element.
Sequences in Rx are described by a push interface(aka callback).
enum Event {
case next(Element)//next element of a sequence
case error(Swift.Error)//sequence failed with error
case completed//sequence terminated successfully
}

class Observable {
func subscribe(_ observer: Observer) -> Disposable
}

protocol ObserverType {
func on(_ event: Event)
}
When a sequence sends the completed or error event all internal resources that compute sequence elements will be freed.
To cancel production of sequence elements and free resources immediately,call dispose on the returned subscription.
If a sequence terminates in finite time, not calling dispose or not using disposed(by:disposeBag)won’t cause any permanent resource leaks.However,those resources will be used until the sequence completes,either by finishing production of elements or returning an error.
If a sequence does not terminate on its own,such as with a series of button taps,resources will be allocated permanently unless dispose is called manually,automatically inside of a disposeBag,with the takeUntil operator,or in some other way.
Using dispose bags or takeUntil operator is a robust way of making sure resources are cleaned up.We recommend using them in production event if the sequences will terminate in finite time.
If your are curious why swift.error isn’t generic,you can find the explanation here.
Disposing
There is on additional way an observed sequence can terminate.When w2e are done with a sequence and we want to release all of the resources allocated to compute the upcoming elements,we can call dispose on a subscription.
Here is an example with the interval operator.
let scheduler = SerialDispatchQueueScheduler(qos: .default)
let subscription = Observable.interval(0.3, scheduler: schedule)
.subscribe { event in
print(event)
}
Thread.sleep(forTimeInterval: 2.0)
subscription.dispose()
This will print:
012345
Note that you usually do not want to manually call dispose;this is only an educational example.Calling dispose manually is usually a bad code smell.There are better ways to dispose of subscirptions such as DisposeBag,the takeUntil operator,or some other mechanism.
So can this code print something after the dispose call is executed?The answer is: it depends.
If the scheduler is a serial scheduler(ex.MainScheduler)and dispose is called on the same serial scheduler,the answer is no.
otherwise it is yes.
You can find out more about schedulers here.
You simply have two processes happending in parallel.
.one is producing elements
.the othere is disposing of the subscription
The question ‘Can something be printed after?’does not even make sense in the case that those processes are on different schedulers.
A few more examples just to be sure (observeOn is explained here).
In case we have something like:
let subscription = Observable.interval(0.3, scheduler: scheduler)
.observeOn(MainScheduler.instance)
.subscribe{ event in
print(event)
}
subscription.dispose()//called from main thread
After this dispose call returns,nothing will be printed.That is graranteed.Also,in this case:
let subscription = Observable.interval(0.3, scheduler: scheduler)
.observeOn(serialScheduler)
.subscribe { event in
print (event)
}
subscription.dispose//executing on smae serialScheduler’
After the dispose call returns, noting will be printed.That is guaranteed.
Dispose Bags
Dispose bags are used to return ARC like behavior to RX.
When a DisposeBag is deallocated,it will call dispose on each of the added disposables.
It does not have a dispose method and therefore does not allow calling explicit dispose on purpose.If immediate cleanup is required,we can just create a new bag.
self.disposeBag = DisposeBag()
This will clear old references and cause disposal of resources.
If that explicit manual disposal is still wanted,use compositeDisposable.It has the wanted behavior but once that dispose method is called,it will immediately dispose any newly added disposable.
Take until
Additional way to automatically dispose subscription on dealloc is to use takeUntil operator.
sequence
.takeUntil(self.rx.deallocated)
.subscribe {
print($0)
}

Implicit Observable guarantees
There is also a couple of additional guarantees that all sequence producers(Observable s)must honor.
It doesn’t matter on which thread they produce elements,but if they generate one elemtn and send it to the observer observe.on(.next(nextElement)),they can’t send next element until observer.on method has finishedc execution.
Producer also cannot send terminating .completed or .error in case .next event hasn’t finished.
In short,consider this example:
someObservable
.subscribe { (e: Event) in
print(“Event processing started”)
//processing
print(“Event processing ended”)
}
This will always print:
Event processing started
Event processing ended
Event processing started
Event processing ended
Event processing started
Event processing ended

Creating your own Observable(aka observable sequence)
There is one crucial thing to understand about observables.
When an observable is created,it doesn’t perform any work simply because it has been created.
It is true that observable can generate elements in may ways.Some of them cause side effects and some of them atp into existing runing processes like tapping into mouse events,etc.
However,if you just call a method that returns an observable,nosequence generation is performed and there are no side effects.observable just defines how the sequence is generated and what parameters are used for element generation.Sequence generation starts when subscribe method is called.
Let’s say you have a method with similar prototype:

func searchWikipedia(searchTerm: String) ->Observable{}
let searchForMe = searchWikipedial(“me”)
//no request are performed,no work is beging done ,no url requests were fired
let cancel = searchForMe
//sequence generation starts now URL requests are fired
.subscirbe(onNext: {results in
print(results)
})

func myJust(_ element: E) -> Observable {
return Observable.create { observer in
observer.on(.next(element))
observer.on(.completed))
return Disposables.create()
}
}
myJust(0)
.subscirbe(onNext: { n in
print(n)
}
This will print: 0
Not bad. So what is the create function?

func myFrom(_ sequence: [E]) -> Observable {
return Observable.create { observer in
for element in sequence {
observer.on(.next(element))
}
observer.on(.completed)
return Disposables.create()
}
}

let stringCounter = myFrom([“first”, “second”])
print(“started————–”)
//first time
stringCounter
.subscribe(onNext: { n in
print(n)
})
print(“——–”)
//again
stringCounter
.subscirbe(onNext: { n in
print(n)
})
print(“Ended—-“)
please input the answer ???