响应式框架 RxSwift

iOS开发中,比较常用的响应式框架有两个.一个是OC版本的ReactiveCocoa,另一个是Swift版本的RxSwift:

  1. ReactiveCocoa
    简称RAC,有Objective-C , Swift版本

  2. ReactiveX
    简称Rx,有众多编程语言版本,比如RxJava , RxKotlin , RxJS , RxCpp , RxPHP , RxgGo , RxSwift等等.

从上面可以看出Rx支持的语言种类更多,它的API和维护也更加完善,所以我们主要了解掌握Rx就好.

首先我们使用CocoaPods导入RxSwift:


pod 'RxSwift','~>5'
pod 'RxCocoa','~>5'

RxSwift : 是Rx标准APISwift实现,不包括任何iOS相关的内容.
RxCocoa : 基于RxSwift,给iOS UI控件扩展了很多Rx特性.

RxSwift核心角色
  1. Observable : 负责发送事件 ( Event )

  2. Observer : 负责订阅Observable,监听Observable发送的事件 ( Event )

事件 ( Event )

RxSwift中的事件就是一个枚举类型,如下:


public enum Event<Element> {
    /// Next element is produced.
    case next(Element)

    /// Sequence terminated with an error.
    case error(Swift.Error)

    /// Sequence completed successfully.
    case completed
}

next : 携带的具体数据.
error : 携带错误信息,表明Observable终止,不会再发出事件.
completed : 表明Observable正常终止,不会再发出事件.

Observable相当于一个发射器,向外发送事件,我们看看怎么创建一个Observable:

创建Observable的五种方式
创建方式一: 使用create方法创建:

        let observable = Observable<Int>.create { (observer) -> Disposable in
            return Disposables.create()
        }

create方法闭包参数要求返回一个Disposable实例所以在闭包中必须返回return Disposables.create(),这是一个固定写法.

创建好了Observable,怎么发送消息呢?

create的闭包参数中会传入一个AnyObserver参数,直接使用Observer发送事件( 数据 ):


        let observable = Observable<Int>.create { (observer) -> Disposable in
            //发送事件
            observer.onNext(1)
            observer.onNext(2)
            observer.onNext(3)
            observer.onNext(4)
            return Disposables.create()
        }

创建方式二: 发送单次消息

let observable2 = Observable.just(1)

使用just方式创建Observable传入事件,并且会单次发送消息,相当于:


        let observable1 = Observable<Int>.create { (observer) -> Disposable in
            //发送单次消息
            observer.onNext(1)
            observer.onCompleted()
            return Disposables.create()
        }


创建方式三: 发送多次消息

let observable3 = Observable.of(1,2,3,4)

创建方式四: 发送多次消息,数组形式

let observable4 = Observable.from([1,2,3,4])

创建方式五: 定时器

// 2s 之后,每隔1s,在主线程
let observable5 = Observable<Int>.timer(.seconds(5), period: .seconds(1), scheduler: MainScheduler.instance)

上面已经完成了事件/数据的发送,接下来就该订阅数据了.

订阅事件的方式有三种:
订阅方式一: 直接使用闭包表达式订阅:

        observable.subscribe { event in
            switch event{
            case .next(let element):
                print("next",element)
            case .error(let error):
                print("error",error)
            case .completed:
                print("completed")
            }

// 打印效果
next 1
next 2
next 3
next 4
订阅方式二: 将事件拆分开:

        observable.subscribe(onNext: { (element) in
            print(element)
        }, onError: { (error) in
            print(error)
        }, onCompleted: {
            print("complement")
        }, onDisposed: {
            print("onDisposed")
        })

订阅方式三: 通过Observer方式订阅:

        //先创建一个 observer
        let observer = AnyObserver<Int>.init { (element) in
            switch element{
            case .next(let element):
                print("next",element)
            case .error(let error):
                print("error",error)
            case .completed:
                print("completed")
            }
        }
        
        observable.subscribe(observer)

Observer除了AnyObserver这种类型外还有Binder类型.

现在我们创建Binder类型的Observer,并且实现订阅:


// 2s 之后,每隔1s,在主线程
let observable5 = Observable<Int>.timer(.seconds(2), period: .seconds(1), scheduler: MainScheduler.instance)

// 创建 observer
let bind = Binder<String>.init(label) { (label, str) in
            label.text = str
}
        
//订阅消息
observable5.map{"\($0)"}.subscribe(bind)


//直接通过 bind:to 绑定 和 subscribe 等价
observable5.map{"数字是:\($0)"}.bind(to: label.rx.text)

Binder
内存管理

每当Observable被订阅时,都会返回一个Disposable实例,当调用Disposabledispose时,就相当于取消订阅.

一共有三种方式可以取消订阅:

取消方式一: dispose()

调用Disposabledispose()方法.


         observable1.subscribe { (event) in
            switch event{
            case .next(let event):
                print("nect : ", event)
            case .error(let error):
                print("error : ", error)
            case .completed:
                print("completed")
            }
        }.dispose()


上面直接调用dispose立马取消订阅,相当于只订阅一次.
也可以将dispose声明成一个属性,在需要取消订阅的时候取消.


        var disposable: Disposable!

        //订阅
        disposable = observable1.subscribe { (event) in
            switch event{
            case .next(let event):
                print("nect : ", event)
            case .error(let error):
                print("error : ", error)
            case .completed:
                print("completed")
            }
        }
        
        //取消订阅
        disposable.dispose()


取消方式二: bag

第二种方式是采用垃圾回收袋的方式bag


         var bag = DisposeBag()

        //订阅
        observable1.subscribe { (event) in
            switch event{
            case .next(let event):
                print("nect : ", event)
            case .error(let error):
                print("error : ", error)
            case .completed:
                print("completed")
            }
        }.disposed(by: bag)


采用bag方式意思是当bag销毁的时候,会自动将放入bagDisposable的实例dispose.

取消方式三: takeUntil

        observable1.takeUntil(self.rx.deallocated).subscribe { (event) in
            switch event{
            case .next(let event):
                print("nect : ", event)
            case .error(let error):
                print("error : ", error)
            case .completed:
                print("completed")
            }
        }

untilself销毁( deinit )时,会自动调用Disposable实例的dispose.

takeUntil后面要求传入一个ObservableType.上面示例代码传入的是当前ViewController.为什么ViewController能调用出rx呢?是不是看的有些眼熟,这正是我们在面向协议编程 中讲的给系统类添加前缀的方法.

Binder

上面已经讲过Binder是另一种常用的Observer.其实Binder在响应式编程中经常被用到,特别是在绑定一些控件时.

比如倒计时功能:


        var disposable: Disposable!

        disposable = observable3.map({ num in
            print(num)
            if num == 11 {
                self.disposable.dispose()
            }
            if num == 10 {
                return "重试"
            }
            return "\(self.time - num)"
            }).bind(to: bind3)

上面代码从10倒计时到0时取消订阅,并且label字体更改为重试.

我们可以对上面的代码进行优化,我们知道.bind()后面跟的是一个ObserverType类型.而ObserverType有两种:AnyObserverBinder.

现在我们的目的是把倒计时的数字和Labeltext绑定起来,所以我们下手点是扩展一个ObserverType类型的label.text.

这就用到面向协议编程的概念了.

首先我们知道RxCocoa已经对所有的 iOS UI控件扩展了很多Rx特性.

比如UILable就能调用rx:

可以看到rxReactive<>类型,所以我们要给Reactive扩展功能:


//为Reactive扩展功能
extension Reactive where Base: UILabel{
    //扩展一个类型为 Binder<String> 的 myText 计算属性
    var myText: Binder<String>{
        //在 Binder 的 init 方法内部,直接把监听到的值赋值给 label.text
        Binder<String>.init(base) { (base, str) in
            base.text = str
        }
    }
}

扩展完成后就能直接绑定,不需要再单独创建Binder了:


        //优化倒计时,
        
       let observable3 = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance)

        disposable = observable3.map({ num in
            print(num)
            if num == 11 {
                self.disposable.dispose()
            }
            if num == 10 {
                return "重试"
            }
            return "\(self.time - num)"
        }).bind(to: label.rx.myText)

其实我们自己为UILabel扩展的myText属性,Rx早就为我们扩展好了:

Rx为我们扩展的 text

不但如此,Rx几乎把所有UI控件的属性都扩展了:

Rx 扩展的其他属性

iOS开发中经常要对各种状态进行监听,常见的监听方案有:

  • KVO
  • Target-Action
  • Notification
  • Delegate
  • Block 回调

我们学习了Rx后,就多了一种监听方案,比如用Rx替换Target-Action.

常规的Target-Action做法:


button.addTarget(self, action: #selector(test), for: .touchUpInside)

@objc func test(){
    print("按钮被点击了")
}

使用Rx监听按钮状态:


let observable = button.rx.controlEvent(.touchUpInside)
        
observable.subscribe(onNext: {
       print("按钮被点击了")
})

Rx实现KVO:


        class Student: NSObject {
            @objc dynamic var name: String?
        }


        var xiaoMing = Student()
        xiaoMing.rx.observe(String.self, "name").subscribe(onNext: { (name) in
            print("name is ",name ?? "nil")
        })
        
        xiaoMing.name = "Jack"
        xiaoMing.name = "Tom"

Rx实现通知:


        NotificationCenter.default.rx.notification(UIApplication.didEnterBackgroundNotification).subscribe(onNext: {(notificaton) in
            print("程序进入后台",notificaton)
        })

既是Observable又是Observer

Rx中有些对象既被当作Observable使用,也可当做Observer使用,比如Slider:

slider 当做 observer 使用
slider 作为 Observable 发送消息使用

slider.rx.valueControlProperty类型:

ControlProperty又遵守了ObservableType, ObserverType协议:

所以ControlProperty既可当Observable也可当Observer使用.

textField.rx.text也是ControlProperty类型,我们要想监听textField文字改变就简单很多:


        //监听 textField 文字改变
        
        textField.rx.text.subscribe(onNext: { (text) in
            print(text)
            }).disposed(by: bag)

Rx配合TableView使用:


        // 数据就是 Observable
        var students = Observable.just([
        Student(name: "张三", age: 18),
        Student(name: "李四", age: 19),
        Student(name: "王五", age: 20)
        ])
        
        // 把数据绑定到 cell 上, Rx 把 cell 封装成了 items
        students.bind(to: tableView.rx.items(cellIdentifier: "cell")){
            row, stu, cell in
            cell.textLabel?.text = stu.name
            cell.detailTextLabel?.text = "\(stu.age)"
            
        }.disposed(by: bag)
        
        //监听 tableView 点击, 回传 点击的具体对象
        tableView.rx.modelSelected(Student.self).subscribe(onNext: { (student) in
            print(student)
        })
        
        //监听 tableView 点击, 回传 点击的 indexPath
        tableView.rx.itemSelected.subscribe(onNext: { (indexPath) in
            print(indexPath)
        })

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容