RxSwift学习之旅-初见RxSwift

作者: ripple_k | 来源:发表于2017-05-24 18:20 被阅读676次

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

    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

    相关文章

      网友评论

      本文标题:RxSwift学习之旅-初见RxSwift

      本文链接:https://www.haomeiwen.com/subject/hxjgxxtx.html