大神都在看的RxSwift 的完全入坑手册

2015-09-24 18:25 CallMeWhy callmewhy 字号:T | T

我主要是通过项目里的 Rx.playground 进行学习和了解的,这种方式确实便捷高效。只需要把文档用 /*: */ 注释即可,直接用 Markdown 编写,简单方便。不过 Xcode7 中这种方式现在还不是很稳定,会有大量的空行,而且有个最大的问题就是阅读到中间然后切到其他文件再切回来的时候,阅读的进度条是从头开始的,并不能记录上次阅读的位置。心累。............

AD:51CTO技术沙龙 | 赋予APP不同凡响的交互和体验>>

RxSwift 是我在 Github 上关注已久的一个项目,今天花点时间过了一下它的示例代码,感觉很有意思。

我主要是通过项目里的 Rx.playground 进行学习和了解的,这种方式确实便捷高效。只需要把文档用 /*: */ 注释即可,直接用 Markdown 编写,简单方便。不过 Xcode7
中这种方式现在还不是很稳定,会有大量的空行,而且有个最大的问题就是阅读到中间然后切到其他文件再切回来的时候,阅读的进度条是从头开始的,并不能记录上次阅读的位置。心累。

下面是我的简单笔记,只是把学习过程中的收获记录下来,大部分内容来自于项目内的 playground 。注意!是很大部分!而且操场里图文并茂,很容易理解。所以,各位如果感兴趣,建议 clone 官方项目,跑个操场玩玩。

参考文献中罗列了我在学习过程中查阅的相关资料,可以作为补充阅读。

SupportCode

在进入正题之前,先看下项目里的 SupportCode.swift ,主要为 playground 提供了两个便利函数。

一个是 example 函数,专门用来写示例代码的,统一输出 log 便于标记浏览,同时还能保持变量不污染全局:

  1. public func example(description: String, action: () -> ()) {
  2. print("\n--- \(description) example ---")
  3. action()
  4. }

另一个是 delay 函数,通过 dispatch_after 用来演示延时的:

  1. public func delay(delay:Double, closure:()->()) {
  2. dispatch_after(
  3. dispatch_time(
  4. DISPATCH_TIME_NOW,
  5. Int64(delay * Double(NSEC_PER_SEC))
  6. ),
  7. dispatch_get_main_queue(), closure)
  8. }

Introduction

主要介绍了 Rx 的基础: Observable 。 Observable<Element> 是观察者模式中被观察的对象,相当于一个事件序列 (GeneratorType) ,会向订阅者发送新产生的事件信息。事件信息分为三种:

  • .Next(value) 表示新的事件数据。
  • .Completed 表示事件序列的完结。
  • .Error 同样表示完结,但是代表异常导致的完结。

(打个岔:协议命名,想起来上午汤哥在微博说的一段话:

另外,我觉得 protocol 名字用形容词会更加语义分明,比如 Swift : Flyable, Killable, Visible。全用名词的话显得比较生硬,比如 Swift : Head, Wings, Ass。

empty

empty 是一个空的序列,它只发送 .Completed 消息。

  1. example("empty") {
  2. let emptySequence: Observable<Int> = empty()
  3. let subscription = emptySequence
  4. .subscribe { event in
  5. print(event)
  6. }
  7. }
  8. --- empty example ---
  9. Completed

never

never 是没有任何元素、也不会发送任何事件的空序列。

  1. example("never") {
  2. let neverSequence: Observable<String> = never()
  3. let subscription = neverSequence
  4. .subscribe { _ in
  5. print("This block is never called.")
  6. }
  7. }
  8. --- never example ---

just

just 是只包含一个元素的序列,它会先发送 .Next(value) ,然后发送 .Completed 。

  1. example("just") {
  2. let singleElementSequence = just(32)
  3. let subscription = singleElementSequence
  4. .subscribe { event in
  5. print(event)
  6. }
  7. }
  8. --- just example ---
  9. Next(32)
  10. Completed

sequenceOf

sequenceOf 可以把一系列元素转换成事件序列。

 
  1. example("sequenceOf") {
  2. let sequenceOfElements/* : Observable<Int> */ = sequenceOf(0, 1, 2, 3)
  3. let subscription = sequenceOfElements
  4. .subscribe { event in
  5. print(event)
  6. }
  7. }
  8. --- sequenceOf example ---
  9. Next(0)
  10. Next(1)
  11. Next(2)
  12. Next(3)
  13. Completed

form

form 是通过 asObservable() 方法把 Swift 中的序列 (SequenceType) 转换成事件序列。

  1. example("from") {
  2. let sequenceFromArray = [1, 2, 3, 4, 5].asObservable()
  3. let subscription = sequenceFromArray
  4. .subscribe { event in
  5. print(event)
  6. }
  7. }
  8. --- from example ---
  9. Next(1)
  10. Next(2)
  11. Next(3)
  12. Next(4)
  13. Next(5)
  14. Completed

create

create 可以通过闭包创建序列,通过 .on(e: Event) 添加事件。

  1. example("create") {
  2. let myJust = { (singleElement: Int) -> Observable<Int> in
  3. return create { observer in
  4. observer.on(.Next(singleElement))
  5. observer.on(.Completed)
  6. return NopDisposable.instance
  7. }
  8. }
  9. let subscription = myJust(5)
  10. .subscribe { event in
  11. print(event)
  12. }
  13. }
  14. --- create example ---
  15. Next(5)
  16. Completed

failWith

failWith 创建一个没有元素的序列,只会发送失败 (.Error) 事件。

  1. example("failWith") {
  2. let error = NSError(domain: "Test", code: -1, userInfo: nil)
  3. let erroredSequence: Observable<Int> = failWith(error)
  4. let subscription = erroredSequence
  5. .subscribe { event in
  6. print(event)
  7. }
  8. }
  9. --- failWith example ---
  10. Error(Error Domain=Test Code=-1 "The operation couldn’t be completed. (Test error -1.)")

deferred

deferred 会等到有订阅者的时候再通过工厂方法创建 Observable 对象,每个订阅者订阅的对象都是内容相同而完全独立的序列。

  1. example("deferred") {
  2. let deferredSequence: Observable<Int> = deferred {
  3. print("creating")
  4. return create { observer in
  5. print("emmiting")
  6. observer.on(.Next(0))
  7. observer.on(.Next(1))
  8. observer.on(.Next(2))
  9. return NopDisposable.instance
  10. }
  11. }
  12. print("go")
  13. deferredSequence
  14. .subscribe { event in
  15. print(event)
  16. }
  17. deferredSequence
  18. .subscribe { event in
  19. print(event)
  20. }
  21. }
  22. --- deferred example ---
  23. go
  24. creating
  25. emmiting
  26. Next(0)
  27. Next(1)
  28. Next(2)
  29. creating
  30. emmiting
  31. Next(0)
  32. Next(1)
  33. Next(2)

为什么需要 defferd 这样一个奇怪的家伙呢?其实这相当于是一种延时加载,因为在添加监听的时候数据未必加载完毕,例如下面这个例子:

  1. example("TestDeferred") {
  2. var value: String? = nil
  3. var subscription: Observable<String?> = just(value)
  4. // got value
  5. value = "Hello!"
  6. subscription.subscribe { event in
  7. print(event)
  8. }
  9. }
  10. --- TestDeferred example ---
  11. Next(nil)
  12. Completed

如果使用 deffered 则可以正常显示想要的数据:

  1. example("TestDeferred") {
  2. var value: String? = nil
  3. var subscription: Observable<String?> = deferred {
  4. return just(value)
  5. }
  6. // got value
  7. value = "Hello!"
  8. subscription.subscribe { event in
  9. print(event)
  10. }
  11. }
  12. --- TestDeferred example ---
  13. Next(Optional("Hello!"))
  14. Completed

Subjects

接下来是关于 Subject 的内容。 Subject 可以看做是一种代理和桥梁。它既是订阅者又是订阅源,这意味着它既可以订阅其他 Observable 对象,同时又可以对它的订阅者们发送事件。

如果把 Observable 理解成不断输出事件的水管,那 Subject 就是套在上面的水龙头。它既怼着一根不断出水的水管,同时也向外面输送着新鲜水源。如果你直接用水杯接着水管的水,那可能导出来什么王水胶水完全把持不住;如果你在水龙头下面接着水,那你可以随心所欲的调成你想要的水速和水温。

(好吧上面一段文档里没有,是我瞎掰的,如果理解错了还望打脸( ̄ε(# ̄)☆╰╮( ̄▽ ̄///))

在开始下面的代码之前,先定义一个辅助函数用于输出数据:

  1. func writeSequenceToConsole<O: ObservableType>(name: String, sequence: O) {
  2. sequence
  3. .subscribe { e in
  4. print("Subscription: \(name), event: \(e)")
  5. }
  6. }

PublishSubject

PublishSubject 会发送订阅者从订阅之后的事件序列。

  1. example("PublishSubject") {
  2. let subject = PublishSubject<String>()
  3. writeSequenceToConsole("1", sequence: subject)
  4. subject.on(.Next("a"))
  5. subject.on(.Next("b"))
  6. writeSequenceToConsole("2", sequence: subject)
  7. subject.on(.Next("c"))
  8. subject.on(.Next("d"))
  9. }
  10. --- PublishSubject example ---
  11. Subscription: 1, event: Next(a)
  12. Subscription: 1, event: Next(b)
  13. Subscription: 1, event: Next(c)
  14. Subscription: 2, event: Next(c)
  15. Subscription: 1, event: Next(d)
  16. Subscription: 2, event: Next(d)

ReplaySubject

ReplaySubject 在新的订阅对象订阅的时候会补发所有已经发送过的数据队列,bufferSize 是缓冲区的大小,决定了补发队列的最大值。如果 bufferSize 是1,那么新的订阅者出现的时候就会补发上一个事件,如果是2,则补两个,以此类推。

  1. example("ReplaySubject") {
  2. let subject = ReplaySubject<String>.create(bufferSize: 1)
  3. writeSequenceToConsole("1", sequence: subject)
  4. subject.on(.Next("a"))
  5. subject.on(.Next("b"))
  6. writeSequenceToConsole("2", sequence: subject)
  7. subject.on(.Next("c"))
  8. subject.on(.Next("d"))
  9. }
  10. --- ReplaySubject example ---
  11. Subscription: 1, event: Next(a)
  12. Subscription: 1, event: Next(b)
  13. Subscription: 2, event: Next(b) // 补了一个 b
  14. Subscription: 1, event: Next(c)
  15. Subscription: 2, event: Next(c)
  16. Subscription: 1, event: Next(d)
  17. Subscription: 2, event: Next(d)

BehaviorSubject

BehaviorSubject 在新的订阅对象订阅的时候会发送最近发送的事件,如果没有则发送一个默认值。

  1. example("BehaviorSubject") {
  2. let subject = BehaviorSubject(value: "z")
  3. writeSequenceToConsole("1", sequence: subject)
  4. subject.on(.Next("a"))
  5. subject.on(.Next("b"))
  6. writeSequenceToConsole("2", sequence: subject)
  7. subject.on(.Next("c"))
  8. subject.on(.Completed)
  9. }
  10. --- BehaviorSubject example ---
  11. Subscription: 1, event: Next(z)
  12. Subscription: 1, event: Next(a)
  13. Subscription: 1, event: Next(b)
  14. Subscription: 2, event: Next(b)
  15. Subscription: 1, event: Next(c)
  16. Subscription: 2, event: Next(c)
  17. Subscription: 1, event: Completed
  18. Subscription: 2, event: Completed

Variable

Variable 是基于 BehaviorSubject 的一层封装,它的优势是:不会被显式终结。即:不会收到 .Completed 和 .Error 这类的终结事件,它会主动在析构的时候发送 .Complete 。

e 
  1. xample("Variable") {
  2. let variable = Variable("z")
  3. writeSequenceToConsole("1", sequence: variable)
  4. variable.value = "a"
  5. variable.value = "b
  6. writeSequenceToConsole("2", sequence: variable)
  7. variable.value = "c"
  8. }
  9. --- Variable example ---
  10. Subscription: 1, event: Next(z)
  11. Subscription: 1, event: Next(a)
  12. Subscription: 1, event: Next(b)
  13. Subscription: 2, event: Next(b)
  14. Subscription: 1, event: Next(c)
  15. Subscription: 2, event: Next(c)
  16. Subscription: 1, event: Completed
  17. Subscription: 2, event: Completed

Transform

我们可以对序列做一些转换,类似于 Swift 中 CollectionType 的各种转换。在以前的坑中曾经提到过,可以参考:函数式的函数

map

map 就是对每个元素都用函数做一次转换,挨个映射一遍。

  1. example("map") {
  2. let originalSequence = sequenceOf(1,2,3)
  3. originalSequence
  4. .map { $0 * 2 }
  5. .subscribe { print($0) }
  6. }
  7. --- map example ---
  8. Next(2)
  9. Next(4)
  10. Next(6)
  11. Completed

flatMap

map 在做转换的时候很容易出现『升维』的情况,即:转变之后,从一个序列变成了一个序列的序列。

什么是『升维』?在集合中我们可以举这样一个例子,我有一个好友列表 [p1, p2, p3],那么如果要获取我好友的好友的列表,可以这样做:

myFriends.map { $0.getFriends() } 

结果就成了 [[p1-1, p1-2, p1-3], [p2-1], [p3-1, p3-2]] ,这就成了好友的好友列表的列表了。这就是一个『升维』的例子。

(以上内容文档中依旧没有,依旧是我瞎掰的,依旧欢迎有错误当面打脸( ̄ε(# ̄)☆╰╮( ̄▽ ̄///))

在 Swift 中,我们可以用 flatMap 过滤掉 map 之后的 nil 结果。在 Rx 中, flatMap 可以把一个序列转换成一组序列,然后再把这一组序列『拍扁』成一个序列。

  1. example("flatMap") {
  2. let sequenceInt = sequenceOf(1, 2, 3)
  3. let sequenceString = sequenceOf("A", "B", "--")
  4. sequenceInt
  5. .flatMap { int in
  6. sequenceString
  7. }
  8. .subscribe {
  9. print($0)
  10. }
  11. }
  12. --- flatMap example ---
  13. Next(A)
  14. Next(B)
  15. Next(--)
  16. Next(A)
  17. Next(B)
  18. Next(--)
  19. Next(A)
  20. Next(B)
  21. Next(--)
  22. Completed

scan

  1. scan 有点像 reduce ,它会把每次的运算结果累积起来,作为下一次运算的输入值。
  2. example("scan") {
  3. let sequenceToSum = sequenceOf(0, 1, 2, 3, 4, 5)
  4. sequenceToSum
  5. .scan(0) { acum, elem in
  6. acum + elem
  7. }
  8. .subscribe {
  9. print($0)
  10. }
  11. }
  12. --- scan example ---
  13. Next(0)
  14. Next(1)
  15. Next(3)
  16. Next(6)
  17. Next(10)
  18. Next(15)
  19. Completed

Filtering

除了上面的各种转换,我们还可以对序列进行过滤。

filter

filter 只会让符合条件的元素通过。

  1. example("filter") {
  2. let subscription = sequenceOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
  3. .filter {
  4. $0 % 2 == 0
  5. }
  6. .subscribe {
  7. print($0)
  8. }
  9. }
  10. --- filter example ---
  11. Next(0)
  12. Next(2)
  13. Next(4)
  14. Next(6)
  15. Next(8)
  16. Completed

distinctUntilChanged

  1. distinctUntilChanged 会废弃掉重复的事件。
  2. example("distinctUntilChanged") {
  3. let subscription = sequenceOf(1, 2, 3, 1, 1, 4)
  4. .distinctUntilChanged()
  5. .subscribe {
  6. print($0)
  7. }
  8. }
  9. --- distinctUntilChanged example ---
  10. Next(1)
  11. Next(2)
  12. Next(3)
  13. Next(1)
  14. Next(4)
  15. Completed 

take

take 只获取序列中的前 n 个事件,在满足数量之后会自动 .Completed 。

  1. example("take") {
  2. let subscription = sequenceOf(1, 2, 3, 4, 5, 6)
  3. .take(3)
  4. .subscribe {
  5. print($0)
  6. }
  7. }
  8. --- take example ---
  9. Next(1)
  10. Next(2)
  11. Next(3)
  12. Completed

Combining

这部分是关于序列的运算,可以将多个序列源进行组合拼装成一个新的事件序列。

startWith

  1. startWith 会在队列开始之前插入一个事件元素。
  2. example("startWith") {
  3. let subscription = sequenceOf(4, 5, 6)
  4. .startWith(3)
  5. .subscribe {
  6. print($0)
  7. }
  8. }
  9. --- startWith example ---
  10. Next(3)
  11. Next(4)
  12. Next(5)
  13. Next(6)
  14. Completed
 

combineLatest

如果存在两条事件队列,需要同时监听,那么每当有新的事件发生的时候,combineLatest 会将每个队列的最新的一个元素进行合并。

 
  1. example("combineLatest 1") {
  2. let intOb1 = PublishSubject<String>()
  3. let intOb2 = PublishSubject<Int>()
  4. combineLatest(intOb1, intOb2) {
  5. "\($0) \($1)"
  6. }
  7. .subscribe {
  8. print($0)
  9. }
  10. intOb1.on(.Next("A"))
  11. intOb2.on(.Next(1))
  12. intOb1.on(.Next("B"))
  13. intOb2.on(.Next(2))
  14. }
  15. --- combineLatest 1 example ---
  16. Next(A 1)
  17. Next(B 1)
  18. Next(B 2)

zip

  1. zip 人如其名,就是压缩两条队列用的,不过它会等到两个队列的元素一一对应地凑齐了之后再合并。
  2. example("zip 1") {
  3. let intOb1 = PublishSubject<String>()
  4. let intOb2 = PublishSubject<Int>()
  5. zip(intOb1, intOb2) {
  6. "\($0) \($1)"
  7. }
  8. .subscribe {
  9. print($0)
  10. }
  11. intOb1.on(.Next("A"))
  12. intOb2.on(.Next(1))
  13. intOb1.on(.Next("B"))
  14. intOb1.on(.Next("C"))
  15. intOb2.on(.Next(2))
  16. }
  17. --- zip 1 example ---
  18. Next(A 1)
  19. Next(B 2)
 

marge

merge 就是 merge 啦,把两个队列按照顺序组合在一起。

  1. example("merge 1") {
  2. let subject1 = PublishSubject<Int>()
  3. let subject2 = PublishSubject<Int>()
  4. sequenceOf(subject1, subject2)
  5. .merge()
  6. .subscribeNext { int in
  7. print(int)
  8. }
  9. subject1.on(.Next(1))
  10. subject1.on(.Next(2))
  11. subject2.on(.Next(3))
  12. subject1.on(.Next(4))
  13. subject2.on(.Next(5))
  14. }
  15. --- merge 1 example ---
  16. 1
  17. 2
  18. 3
  19. 4
  20. 5

switch

当你的事件序列是一个事件序列的序列 (Observable<Observable<T>>) 的时候,(可以理解成二维序列?),可以使用 switch 将序列的序列平铺成一维,并且在出现新的序列的时候,自动切换到最新的那个序列上。和 merge 相似的是,它也是起到了将多个序列『拍平』成一条序列的作用。

  1. example("switchLatest") {
  2. let var1 = Variable(0)
  3. let var2 = Variable(200)
  4. // var3 is like an Observable<Observable<Int>>
  5. let var3 = Variable(var1)
  6. let d = var3
  7. .switchLatest()
  8. .subscribe {
  9. print($0)
  10. }
  11. var1.value = 1
  12. var1.value = 2
  13. var1.value = 3
  14. var1.value = 4
  15. var3.value = var2
  16. var2.value = 201
  17. var1.value = 5
  18. var3.value = var1
  19. var2.value = 202
  20. var1.value = 6
  21. }
  22. --- switchLatest example ---
  23. Next(0)
  24. Next(1)
  25. Next(2)
  26. Next(3)
  27. Next(4)
  28. Next(200)
  29. Next(201)
  30. Next(5)
  31. Next(6)

注意,虽然都是『拍平』,但是和 flatmap 是不同的, flatmap 是将一条序列变成另一条序列,而这变换过程会让维度变高,所以需要『拍平』,而 switch 是将本来二维的序列(序列的序列)拍平成了一维的序列。

Error Handling

在事件序列中,遇到异常也是很正常的事情,有以下几种处理异常的手段。

catchError

catchError 可以捕获异常事件,并且在后面无缝接上另一段事件序列,丝毫没有异常的痕迹。

  1. example("catchError 1") {
  2. let sequenceThatFails = PublishSubject<Int>()
  3. let recoverySequence = sequenceOf(100, 200)
  4. sequenceThatFails
  5. .catchError { error in
  6. return recoverySequence
  7. }
  8. .subscribe {
  9. print($0)
  10. }
  11. sequenceThatFails.on(.Next(1))
  12. sequenceThatFails.on(.Next(2))
  13. sequenceThatFails.on(.Error(NSError(domain: "Test", code: 0, userInfo: nil)))
  14. }
  15. --- catchError 1 example ---
  16. Next(1)
  17. Next(2)
  18. Next(100)
  19. Next(200)
  20. Completed

retry

retry 顾名思义,就是在出现异常的时候会再去从头订阅事件序列,妄图通过『从头再来』解决异常。

  1. example("retry") {
  2. var count = 1 // bad practice, only for example purposes
  3. let funnyLookingSequence: Observable<Int> = create { observer in
  4. let error = NSError(domain: "Test", code: 0, userInfo: nil)
  5. observer.on(.Next(0))
  6. observer.on(.Next(1))
  7. if count < 2 {
  8. observer.on(.Error(error))
  9. count++
  10. }
  11. observer.on(.Next(2))
  12. observer.on(.Completed)
  13. return NopDisposable.instance
  14. }
  15. funnyLookingSequence
  16. .retry()
  17. .subscribe {
  18. print($0)
  19. }
  20. }
  21. --- retry example ---
  22. Next(0)
  23. Next(1)
  24. Next(0)
  25. Next(1)
  26. Next(2)
  27. Completed

Utility

这里列举了针对事件序列的一些方法。

subscribe

subscribe 在前面已经接触过了,有新的事件就会触发。

  1. example"subscribe") {
  2. let sequenceOfInts = PublishSubject<Int>()
  3. sequenceOfInts
  4. .subscribe {
  5. print($0)
  6. }
  7. sequenceOfInts.on(.Next(1))
  8. sequenceOfInts.on(.Completed)
  9. }
  10. --- subscribe example ---
  11. Next(1)
  12. Completed
  13. subscribeNext
  14. subscribeNext 也是订阅,但是只订阅 .Next 事件。
  15. example("subscribeNext") {
  16. let sequenceOfInts = PublishSubject<Int>()
  17. sequenceOfInts
  18. .subscribeNext {
  19. print($0)
  20. }
  21. sequenceOfInts.on(.Next(1))
  22. sequenceOfInts.on(.Completed)
  23. }
  24. --- subscribeNext example ---
  25. 1
 

subscribeCompleted

  1. subscribeCompleted 是只订阅 .Completed 完成事件。
  2. example("subscribeCompleted") {
  3. let sequenceOfInts = PublishSubject<Int>()
  4. sequenceOfInts
  5. .subscribeCompleted {
  6. print("It's completed")
  7. }
  8. sequenceOfInts.on(.Next(1))
  9. sequenceOfInts.on(.Completed)
  10. }
  11. --- subscribeCompleted example ---
  12. It's completed

subscribeError

  1. subscribeError 只订阅 .Error 失败事件。
  2. example("subscribeError") {
  3. let sequenceOfInts = PublishSubject<Int>()
  4. sequenceOfInts
  5. .subscribeError { error in
  6. print(error)
  7. }
  8. sequenceOfInts.on(.Next(1))
  9. sequenceOfInts.on(.Error(NSError(domain: "Examples", code: -1, userInfo: nil)))
  10. }
  11. --- subscribeError example ---
  12. Error Domain=Examples Code=-1 "The operation couldn’t be completed. (Examples error -1.)"
 

doOn

  1. doOn 可以监听事件,并且在事件发生之前调用。
  2. example("doOn") {
  3. let sequenceOfInts = PublishSubject<Int>()
  4. sequenceOfInts
  5. .doOn {
  6. print("Intercepted event \($0)")
  7. }
  8. .subscribe {
  9. print($0)
  10. }
  11. sequenceOfInts.on(.Next(1))
  12. sequenceOfInts.on(.Completed)
  13. }
  14. --- doOn example ---
  15. Intercepted event Next(1)
  16. Next(1)
  17. Intercepted event Completed
  18. Completed
 

Conditional

我们可以对多个事件序列做一些复杂的逻辑判断。

takeUntil

takeUntil 其实就是 take ,它会在终于等到那个事件之后触发 .Completed 事件。

 
  1. example("takeUntil") {
  2. let originalSequence = PublishSubject<Int>()
  3. let whenThisSendsNextWorldStops = PublishSubject<Int>()
  4. originalSequence
  5. .takeUntil(whenThisSendsNextWorldStops)
  6. .subscribe {
  7. print($0)
  8. }
  9. originalSequence.on(.Next(1))
  10. originalSequence.on(.Next(2))
  11. whenThisSendsNextWorldStops.on(.Next(1))
  12. originalSequence.on(.Next(3))
  13. }
  14. --- takeUntil example ---
  15. Next(1)
  16. Next(2)
  17. Completed

takeWhile

takeWhile 则是可以通过状态语句判断是否继续 take 。

 
  1. example("takeWhile") {
  2. let sequence = PublishSubject<Int>()
  3. sequence
  4. .takeWhile { int in
  5. int < 2
  6. }
  7. .subscribe {
  8. print($0)
  9. }
  10. sequence.on(.Next(1))
  11. sequence.on(.Next(2))
  12. sequence.on(.Next(3))
  13. }
  14. --- takeWhile example ---
  15. Next(1)
  16. Completed

Aggregate

我们可以对事件序列做一些集合运算。

concat

concat 可以把多个事件序列合并起来。

 
  1. example("concat") {
  2. let var1 = BehaviorSubject(value: 0)
  3. let var2 = BehaviorSubject(value: 200)
  4. // var3 is like an Observable<Observable<Int>>
  5. let var3 = BehaviorSubject(value: var1)
  6. let d = var3
  7. .concat()
  8. .subscribe {
  9. print($0)
  10. }
  11. var1.on(.Next(1))
  12. var1.on(.Next(2))
  13. var3.on(.Next(var2))
  14. var2.on(.Next(201))
  15. var1.on(.Next(3))
  16. var1.on(.Completed)
  17. var2.on(.Next(202))
  18. }
  19. --- concat example ---
  20. Next(0)
  21. Next(1)
  22. Next(2)
  23. Next(3)
  24. Next(201)
  25. Next(202)

reduce

这里的 reduce 和 CollectionType 中的 reduce 是一个意思,都是指通过对一系列数据的运算最后生成一个结果。

  1. example("reduce") {
  2. sequenceOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
  3. .reduce(0, +)
  4. .subscribe {
  5. print($0)
  6. }
  7. }
  8. --- reduce example ---
  9. Next(45)
  10. Completed

Next

基础入门大概就是这些了,有了前面 《Functional Reactive Programming in Swift - Part 1》 的铺垫,似乎理解起来十分愉快,不过还是不够深入,在下一章会在具体项目中操练起来。

操练起来!跑个操场吧少年!

Run the playground in your Xcode!


参考文献:

大神都在看的RxSwift 的完全入坑手册的更多相关文章

  1. SEO大神都是些什么人

    http://www.wocaoseo.com/thread-97-1-1.html 貌似好久没有更新seo培训联盟的文章了,最近一直在专心学习其他的东西,前一段写了几篇关于用户需求和体验的文章,但是 ...

  2. BAT大神推荐:看懂英文文档,每天只需要10分钟做这件事……

    程序员这个行业是很特殊的.之所以说特殊,就是因为它所有的技术大多来自欧美,所以最主流,最新鲜,最正确的技术文章都是英文,遗憾的是,大部分还没有译本. 有些译文还比较差.与其等待别人的翻译,不如直接阅读 ...

  3. 大神都在用的yum源

    本文原创首发于公众号:编程三分钟 yum 命令的使用 yum命令天天都在用,都快用烂了,但是很多人不知道为什么只要联网,yum命令就能像老奶奶手中的魔法棒一样,随心所欲的下载到想到的包. 比如你想装个 ...

  4. python自带的IDLE编译器,听说大神都用这个(附python下载安装教程)

    python这两年这么火,学的人越来越多,小伙伴们都用什么编译器了? 今天教大家安装python并熟悉python自带的编译器IDLE. 第一步,进入python官网https://www.pytho ...

  5. Go 语言从新手到大神:每个人都会踩的五十个坑(转)

    Go语言是一个简单却蕴含深意的语言.但是,即便号称是最简单的C语言,都能总结出一本<C陷阱与缺陷>,更何况Go语言呢.Go语言中的许多坑其实并不是因为Go自身的问题.一些错误你再别的语言中 ...

  6. 读FCL源码系列之List<T>---让你知其所以然---内含疑问求大神指点

    序言 在.NET开发中,List<T>是我们经常用到的类型.前段时间看到其他部门小伙伴讨论“两个List(10W个元素)集合求并集,list1.Where(p=>list2.Cont ...

  7. MFC基于对话框风格按钮控件添加图片的方法(大神止步)

    菜鸟还在研究这个东西,大神就不要看了.一直都在觉得用VC或VS建立的对话框总是全灰色感觉太单调了,如果可以在上面添加一些漂亮的图片就好了,今天终于实现了.其实挺简单的,下面就分几个步骤讲一下: 第一步 ...

  8. 与大神聊天1h

    与大神聊天1h 啊,与大神聊天1h真的是干货满满 解bug问题 之所以老出bug是因为我老是调用别人的包啊,在调参数的时候,并不知道内部机制 其实就自己写一个函数,然后能把功能实现就好了. 问题是,出 ...

  9. 前端自学vs跟大神系统学?你看着办

    前端自学vs跟大神系统学?你看着办 一名广告专业学生,在大三的时候对于广告行业的前景不是很看好,转而自学web前端,刚开始接触的前端语言是html(html应该不算编程语言),上手很容易,在w3csh ...

随机推荐

  1. 移动端的silder,未封装,基于zepto的touch模块,有参照修改过touch的bug

    <!--html模块--> <header class="appoin-head"> <ul> <li class="aa&qu ...

  2. 【BZOJ4698】Sandy的卡片(后缀数组)

    [BZOJ4698]Sandy的卡片(后缀数组) 题面 讨厌权限题!!! 因为我交不了... 洛谷 题面 做完差之后就是裸的最长公共子串 没了.. 数组往死里开吧... #include<ios ...

  3. 【CJOJ P1957】【NOIP2010冲刺十模拟赛】数字积木

    [NOIP2010冲刺十模拟赛]数字积木 Description 小明有一款新式积木,每个积木上都有一个数,一天小明突发奇想,要是把所有的积木排成一排,所形成的数目最大是多少呢? 你的任务就是读入n个 ...

  4. jsp常见jstl语法(二)

    <c:choose>标签与Javascript switch语句的功能一样,用于在众多选项中做出选择. 语法格式 <c:choose> <c:when test=&quo ...

  5. 如何在原生微信小程序中实现数据双向绑定

    官网:https://qiu8310.github.io/minapp/ 作者:Mora 在原生小程序开发中,数据流是单向的,无法双向绑定,但是要实现双向绑定的功能还是蛮简单的! 下文要讲的是小程序框 ...

  6. 分布式存储系统-HBASE

    简介 HBase –Hadoop Database,是一个高可靠性.高性能.面向列.可伸缩的分布式存储系统,利用HBse技术可在廉价PC Server上搭建起大规模结构化存储集群.HBase利用Had ...

  7. NYOJ街区最短路径问题

    描述 一个街区有很多住户,街区的街道只能为东西.南北两种方向. 住户只可以沿着街道行走. 各个街道之间的间隔相等. 用(x,y)来表示住户坐在的街区. 例如(4,20),表示用户在东西方向第4个街道, ...

  8. 第八届蓝桥杯B组java第四题

    标题:取数位 求1个整数的第k位数字有很多种方法.以下的方法就是一种.对于题目中的测试数据,应该打印5.请仔细分析源码,并补充划线部分所缺少的代码.注意:只提交缺失的代码,不要填写任何已有内容或说明性 ...

  9. Java I/O 总结

    Java I/O的的架构使用了装饰器的模式,我们在使用流的时候需要新建很多的装饰器对象,对源数据进行层层包装.各个包装类名以及它们的应用场景比较多,初学的时候难以摸清规律,这里我把它们归一下类,方便大 ...

  10. 排序算法Java实现(选择排序)

    算法描述:对于给定的一组记录,经过第一轮比较后得到最小的记录,然后将该记录与第一个记录的位置进行交换:接着对不包括第一个记录以外的其他记录进行第二轮比较,得到最小的记录并与第二个记录进行位置交换:重复 ...