RxSwift学习之旅-初见RxSwift

概念性的东西就不在这里做过多的陈述了,在这里只说明两点:

RxSwift究竟是什么

RxSwift is a library for composing asynchronous and event-based code by using observable sequences and functional style operators, allowing for parameterized execution via schedulers.

RxSwift帮我们解决了什么问题

  1. State, and specifically, shared mutable state
  2. Imperative programming
  3. Side effects
  4. Declarative code
  5. Reactive systems

这里只是很简单的介绍了下RxSwift,别问为什么是英文,本人英语水平实在是不敢在这里班门弄斧,同时也避免翻译的不准确给你造成误导。你可以在RxSwift的GitHub上找到更多关于它的详细介绍

要使用RxSwift首先需要了解三样东西

Observables(被观察者)

Observable<T>这个类提供了Rx的基础代码,它能够产生一个异步的事件流去传输关于泛型T类型的相关数据。简单点说就是它能够在当前类订阅(接收到)另外一个类发送数据
Observable<T>这个类也允许多个observers响应事件去更新UI或者去处理接收到的数据
ObservableType这是Observable<T>遵守的一个协议,也就限制了一个Observable只能发出(或者一个observers只能接收)三种类型的事件

  • A next event: 这个事件携带最新的数据,observers通过订阅这个事件接收到最新的数据
  • A completed event: 这个事件表示一个事件序列的完结,observers接收到这个事件后就再也接收不到其它任何事件
  • An error event: 一个事件序列因一个错误而终止,observers接收到这个事件后就再也接收不到其它任何事件

Operators

类似于数学中的+ - * /,Rx也提供了一些方便我们对数据进行处理的操作符

UIDevice.rx.orientation
  .filter { value in
    return value != .landscape
  }.map { _ in
    return "Portrait is the best!"
  }.subscribe(onNext: { string in
    showAlert(text: string)
  })

上面的例子中,每当设备的方向发生改变,我们接收到事件,但是接收到的值并不是我们想要的最终结果,那我们就需要借助Rx中的操作符对数据进行处理

Paste_Image.png
  1. 首先,filter只会允许.landscape通过值。如果设备在横向模式,订阅代码不会执行,因为filter会抑制这些事件
  2. 在value = .portrait的情况下map接收到输入,并把值转换为一个字符串输出的文本"Portrait is the best!"
  3. 最后你订阅next事件,在value = .portrait的情况下将接收到已经处理完成的文本"Portrait is the best!",你可以调用一个方法将这个文本直接显示在屏幕上

Schedulers

Schedulers在Rx中等价于iOS当中的dispatch queues,在Rx中已经帮我们定义好了许多常用Schedulers,这些定义好的Schedulers可以帮助我们解决开发中的大部分问题

  • ConcurrentDispatchQueueScheduler可以并发执行你的代码
  • OperationQueueScheduler可以让你在你给定的队列上运行你的事件序列

在学习Rx基础阶段并不会使用到它,这里只是简单的介绍,如果这系列文章能持续更新的话我打算后面再结合具体事例进行更加详细的讲解

Getting started

了解完过后就可以学习一些Rx的基础代码了,首先创建一个工程,并使用CocoaPods将RxSwift集成到你的项目中,创建一个playground在你的项目中,以便更流畅的书写Rx测试代码,避免过多的command+R浪费很多时间,完成后应该是这样的

Snip20170524_2.png

然后定义这样一个方法,以便我们进行更好的练习

public func example(of description: String, action: () -> Void) {
    print("\n--- Example of:", description, "---")
    action()
}
create

首先我们需要知道What is an observable?
常见的两种方式来创建一个Observable对象,一种是通过引入RxCocoa(RxCocoa是对cocoa进行的Rx扩展),它已经包含了我们常用到的Observable流,比如button的tap事件
let observable = loginButton.rx.tap.asObservable()
也可以使用提供的create函数来创建一个Observable对象

example(of: "create") {
    enum MyError: Error {
        case anError
    }
    
    Observable<String>.create({ (observer) -> Disposable in
        observer.onNext("1")
        observer.onNext("?")
        observer.onCompleted()
//        observer.onError(MyError.anError)
        return Disposables.create()
    }).subscribe({ (event) in
        print(event)
    }).disposed(by: DisposeBag())
}
//--- Example of: create ---
//next(1)
//next(?)
//completed

Disposing

到这里我们必须要知道dispose,在一个observable被订阅前它不做任何事情,subscribe触发observable发送事件直到observable发送.completed.error事件终止,如果我们的subscribe没有收到.completed.error事件那谁来终止这次observable sequence呢?那就是DisposeBag。Rx官方建议我们每一个subscribe都应该dispose,以免造成内存泄漏

example(of: "DisposeBag") {
    let disposeBag = DisposeBag()
    Observable.of("A", "B", "C").subscribe({
        print($0)
    }).addDisposableTo(disposeBag) 
}
![Uploading Snip20170524_14_517335.png . . .]

上述事例当disposeBag生命周期结束时,即使subscribe没有收到.completed.error事件,observable sequence也将会被终止

just
example(of: "What is an observable?") { 
    let one = 1
    let observable: Observable<Int> = Observable<Int>.just(one)
}
Snip20170524_3.png

上面的事例中我们通过Observable.just方法创建了只包含一个元素的observable sequence,我们可以通过订阅它获取到它发送的事件

subscribe
example(of: "What is an observablee?") { 
    let one = 1
    let observable: Observable<Int> = Observable<Int>.just(one)

    observable.subscribe({ (event) in
        print(event)
    })
}
//print:
//--- Example of: What is an observable? ---
//next(1)
//completed
Snip20170524_6.png

这个方法可以订阅到observable发出的任何事件,在上面的例子中我们看到observable发出了两个事件next(1)completed
还有一个跟它很像的订阅方法,也是我们开发中经常用到的订阅方法

Snip20170524_12.png

一目了然,对应的事件在对应的闭包当中响应,需要注意的是这里的参数就不在是事件了,而是事件携带的具体的值。并且对于我们不关注的事件是可以省略的哦~

observable.subscribe(onNext: { (Int) in
        code
    }, onError: { (Error) in
        code
    }, onCompleted: { 
        code
    }, onDisposed: { 
        code
    })
of
example(of: "of") { 
    let one = 1
    let two = 2
    let three = 3
    let observable = Observable.of(one, two, three)
    
    observable.subscribe({ (event) in
        print(event)
    })
}
//print:
//--- Example of: of ---
//next(1)
//next(2)
//next(3)
//completed

Snip20170524_7.png

上面的事例中我们通过Observable.of方法创建了包含三个元素的observable squence,我们通过订阅发现它使用三次next事件依次发送我们给它的三个值,然后发送completed事件告诉我们这个observable的生命周期结束,不会再发送新的值。如果我们将三个值包成一个数组会发生什么呢?

example(of: "of") {
    let one = 1
    let two = 2
    let three = 3
    let observable = Observable.of([one, two, three])
    
    observable.subscribe({ (event) in
        print(event)
    })
}
//print:
//--- Example of: of ---
//next([1, 2, 3])
//completed

他会将这个数组用一个next事件发送给我们,那如果我们开发中需要给他一个数组,让他依次发送给我们应该怎么办呢?

from
example(of: "from") {
    let one = 1
    let two = 2
    let three = 3
    let observable = Observable.from([one, two, three])
    
    observable.subscribe({ (event) in
        print(event)
    })
}
//print:
//--- Example of: from ---
//next(1)
//next(2)
//next(3)
//completed
Snip20170524_8.png

改用from就搞定了,创建observable sequence的方法还有很多,直接上代码吧

empty
example(of: "empty") { //只能收到completed
    let observable = Observable<Void>.empty()
    observable.subscribe({ (event) in
        print(event)
    })
}
//print:
//--- Example of: empty ---
//completed
Snip20170524_9.png
never
example(of: "never") { //不发送任何事件
    let observable = Observable<Any>.never()
    observable.subscribe({ (event) in
        print(event)
    })
}
//print:
Snip20170524_10.png
range
example(of: "range") { 
    let observable = Observable<Int>.range(start: 1, count: 10)
    observable.subscribe(onNext: { (i) in
        print(i)
    })
}
//print:
//--- Example of: range ---
//    1
//    2
//    3
//    4
//    5
//    6
//    7
//    8
//    9
//    10
Snip20170524_11.png
deferred
example(of: "deferred") { 
    let disposeBag = DisposeBag()
    var flip = false
    
    let factory:Observable<Int> = Observable.deferred({ () -> Observable<Int> in
        flip = !flip
        if flip {
            return Observable.of(1,2,3)
        } else {
            return Observable.of(4,5,6)
        }
    })
    for _ in 0...3 {
        factory.subscribe(onNext: {
            print($0, terminator: "")
        }).addDisposableTo(disposeBag)
        print()
    }
}
// --- Example of: deferred ---
//    123
//    456
//    123
//    456

Snip20170524_14.png

Subject

Subject就相当于一个桥梁或者代理,它既可以作为一个observer也可以作为一个Observable

下面来看几种不同的Subject:

PublishSubject

PublishSubject只会发送给订阅者订阅之后的事件,之前发生的事件将不会发送。

example(of: "PublishSubject") {
    let subject = PublishSubject<String>()
    subject.onNext("Is anyone listening?")
    let subscriptionOne = subject.subscribe(onNext: { string in
            print(string)
        })
    subject.on(.next("1"))
    subject.onNext("2")
    
    let subscriptionTwo = subject
        .subscribe { event in
            print("2)", event.element ?? event)
    }
    subject.onNext("3")
    subscriptionOne.dispose()
    subject.onNext("4")
    
    // 1
    subject.onCompleted()
    // 2
    subject.onNext("5")
    // 3
    subscriptionTwo.dispose()
    let disposeBag = DisposeBag()
    // 4
    subject.subscribe {
            print("3)", $0.element ?? $0)
    }.addDisposableTo(disposeBag)
    subject.onNext("?")
}
//--- Example of: PublishSubject ---
//1
//2
//3
//2) 3
//2) 4
//2) completed
//3) completed

如果要保证所有事件都能被订阅到,可以使用Create主动创建或使用ReplaySubject。
如果被观察者因为错误被终止,PublishSubject只会发出一个错误的通知。

ReplaySubject

不管订阅者什么时候订阅的都可以把所有发生过的事件发送给订阅者。

example(of: "ReplaySubject") {
    // 1
    let subject = ReplaySubject<String>.create(bufferSize: 2) //bufferSize指定缓冲区的大小
    let disposeBag = DisposeBag()
    // 2
    subject.onNext("1")
    subject.onNext("2")
    subject.onNext("3")
    // 3
    subject
        .subscribe {
            print(label: "1)", event: $0)
        }
        .addDisposableTo(disposeBag)
    subject
        .subscribe {
            print(label: "2)", event: $0)
        }
        .addDisposableTo(disposeBag)
    
    subject.onNext("4")
    subject.onError(MyError.anError)
    subject.dispose()

    subject
        .subscribe {
            print(label: "3)", event: $0)
    }
        .addDisposableTo(disposeBag)
    
}
//        --- Example of: ReplaySubject ---
//    1) 2
//    1) 3
//    2) 2
//    2) 3
//    1) 4
//    2) 4
//    1) anError
//    2) anError
//    3) Object `RxSwift.ReplayMany<Swift.String>` was already disposed.

BehaviorSubject

广播所有事件给订阅者,对于新的订阅者,广播最近的一个事件或者默认值

// 1
enum MyError: Error {
    case anError
}
// 2
func print<T: CustomStringConvertible>(label: String, event: Event<T>) {
    print(label, event.element ?? event.error ?? event)
}
// 3
example(of: "BehaviorSubject") {
    // 4
    let subject = BehaviorSubject(value: "Initial value")
    let disposeBag = DisposeBag()
    
    subject.subscribe {
            print(label: "1)", event: $0)
        }.addDisposableTo(disposeBag)
    subject.onNext("X")
    // 1
    subject.onError(MyError.anError)
    // 2
    subject.subscribe {
            print(label: "2)", event: $0)
        }.addDisposableTo(disposeBag)
}
//        --- Example of: BehaviorSubject ---
//    1) Initial value
//    1) X
//    1) anError
//    2) anError

PublishSubject, ReplaySubject, and BehaviorSubject当他们被回收时,不会自动发送完成事件

Variable

Variable是BehaviorSubject的封装,它和BehaviorSubject不同之处在于,不能向Variable发送.Complete和.Error,它会在生命周期结束被释放的时候自动发送.Complete

example(of: "Variable") {
    // 1
    var variable = Variable("Initial value")
    let disposeBag = DisposeBag()
    // 2
    variable.value = "New initial value"
    // 3
    variable.asObservable()
        .subscribe {
            print(label: "1)", event: $0)
        }
        .addDisposableTo(disposeBag)
    // 1
    variable.value = "1"
    // 2
    variable.asObservable()
        .subscribe {
            print(label: "2)", event: $0)
        }
        .addDisposableTo(disposeBag)
    // 3
    variable.value = "2"
    
    // These will all generate errors
//    variable.value.onError(MyError.anError)
//    variable.asObservable().onError(MyError.anError)
//    variable.value = MyError.anError
//    variable.value.onCompleted()
//    variable.asObservable().onCompleted()
 
}
//    --- Example of: Variable ---
//1) New initial value
//1) 1
//2) 1
//1) 2
//2) 2

本文参考资料

RxSwift
RxSwift
alonemonkey

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容