美文网首页
RxSwift 入门以及了解

RxSwift 入门以及了解

作者: 蛮荒星域 | 来源:发表于2020-08-27 13:39 被阅读0次

    前言介绍

    RX是一个帮助我们简化异步编程的框架。它拓展了观察者模式,使我们可以自由组合多个异步事件,而不需要去关心线程,同步,线程安全,并发数据以及I/O阻塞。RXSwift是RX的Swift版本。

    为什么要使用RxSwift

    • 复合
    • 复用
    • 清晰
    • 易用 - 因为它抽象了异步编程,使我们统一了代码风格
    • 稳定 - 因为Rx使完全通过单元测试的
    Target Action
    button.rx.tap.subscribe(onNext: {
        print("button Tapped")
    })
    .disposed(by: disposeBag)
    
    代理
    scrollView.rx.contentOffset
                .subscribe(onNext: { contentOffset in
                    print("contentOffset: \(contentOffset)")
                })
                .disposed(by: disposeBag)
    
    闭包回调
    URLSession.shared.rx.data(request: URLRequest(url: url))
        .subscribe(onNext: { data in
            print("Data Task Success with count: \(data.count)")
        }, onError: { error in
            print("Data Task Error: \(error)")
        })
        .disposed(by: disposeBag)
    
    通知
    NotificationCenter.default.rx
            .notification(.UIApplicationWillEnterForeground)
            .subscribe(onNext: { (notification) in
                print("Application Will Enter Foreground")
            })
            .disposed(by: disposeBag)
    

    不需要去管理观察者的生命周期,这样你就有更多精力去关注业务逻辑。

    KVO
    user.rx.observe(String.self, #keyPath(User.name))
            .subscribe(onNext: { newValue in
                print("do something with newValue")
            })
            .disposed(by: disposeBag)
    
    多个任务之间有依赖关系
    /// 用 Rx 封装接口
    enum Api {
    
        /// 通过用户名密码取得一个 token
        static func token(username: String, password: String) -> Observable<String> { ... }
    
        /// 通过 token 取得用户信息
        static func userInfo(token: String) -> Observable<UserInfo> { ... }
    }
    
    /// 通过用户名和密码获取用户信息
    Api.token(username: "beeth0ven", password: "987654321")
        .flatMapLatest(Api.userInfo)
        .subscribe(onNext: { userInfo in
            print("获取用户信息成功: \(userInfo)")
        }, onError: { error in
            print("获取用户信息失败: \(error)")
        })
        .disposed(by: disposeBag)
    
    等待多个并发任务完成后处理结果
    /// 用 Rx 封装接口
    enum Api {
    
        /// 取得老师的详细信息
        static func teacher(teacherId: Int) -> Observable<Teacher> { ... }
    
        /// 取得老师的评论
        static func teacherComments(teacherId: Int) -> Observable<[Comment]> { ... }
    }
    
    /// 同时取得老师信息和老师评论
    Observable.zip(
          Api.teacher(teacherId: teacherId),
          Api.teacherComments(teacherId: teacherId)
        ).subscribe(onNext: { (teacher, comments) in
            print("获取老师信息成功: \(teacher)")
            print("获取老师评论成功: \(comments.count) 条")
        }, onError: { error in
            print("获取老师信息或评论失败: \(error)")
        })
        .disposed(by: disposeBag)
    

    函数响应式编程

    [图片上传失败...(image-6266d-1598506756093)]

    Observable 理解成Signal,订阅Signal去完成一些处理操作。比如绑定数据,最后销毁这个Signal。

    函数式编程是种编程范式,它需要我们将函数作为参数传递,或者作为返回值返还。我们可以通过组合不同的函数来得到想要的结果。

    对每个事件都做出一些响应,所以这种编程叫做函数响应式编程。我们通过不同的构建函数,来创建所需要的数据序列。最后通过适当的方式来响应这个序列。这就是函数响应式编程。

    数据绑定

    数据绑定(订阅):就是指将可被监听的序列绑定到观察者上。

    比如:

    let image: Observable<UIImage> = ...
    image.bind(to: imageView.rx.image)
    

    将一个图片序列 “同步” 到imageView上。这个序列里面的图片可以是异步产生的。这里定义的 image 就是上图中蓝色部分(可被监听的序列)。这种“同步机制”就是数据绑定(订阅)

    RXSwift核心

    [图片上传失败...(image-9ce6ac-1598506756094)]

    • Observable - 产生事件(即信号)
    • Observer - 响应事件(即绑定信号后的处理)
    • Operator - 创建变化组合事件(组合多个信号,并行或者串行)
    • Disposable - 管理绑定(订阅)的生命周期(一般管理信号销毁居多)
    • Schedules - 线程队列调配
    // Observable<String>
    let text = usernameOutlet.rx.text.orEmpty.asObservable()
    
    // Observable<Bool>
    let passwordValid = text
        // Operator
        .map { $0.characters.count >= minimalUsernameLength }
    
    // Observer<Bool>
    let observer = passwordValidOutlet.rx.isHidden
    
    // Disposable
    let disposable = passwordValid
        // Scheduler 用于控制任务在那个线程队列运行
        .subscribeOn(MainScheduler.instance)
        .observeOn(MainScheduler.instance)
        .bind(to: observer)
    
    
    ...
    
    // 取消绑定,你可以在退出页面时取消绑定
    disposable.dispose()
    

    Observable - 可被监听的序列

    所有的事物都是序列,是信号。它能异步的触发一系列事件并携带不可更改的状态变量。简单来说,它能让某个类的实例在一段事件内实现对另一个实例对象值的观察。例如:观察者可以捕获对所有可观察对象触发的事件,从而实现UI的实时更新或者是数据的实时处理。

    subscribe后面的onNext,onError, onCompleted 分别响应。这些为事件。completed事件和error事件都会终止可观察对象的生命周期。不会继续触发新的事件。

    public enum Event<Element> {
        case next(Element)
        case error(Swift.Error)
        case completed
    }
    
    • next - 序列产生了一个新的元素
    • error - 创建序列时产生了一个错误,导致序列终止
    • completed - 序列的所有元素都已经成功产生,整个序列已经完成
    Single
    • 要么产生一个元素,要么产生一个error事件
    • 不会共享状态变化
    Completable
    • 发出零个元素
    • 发出一个completed事件或者一个error事件
    • 不会共享状态变化
    Maybe
    • 发出一个元素或者一个completed事件或者一个error事件
    • 不会共享状态变化
    Driver
    • 不会产生error事件
    • 一定在MainScheduler监听(主线程监听)
    • 共享状态变化
    ControlEvent

    专门用于描述UI控件所产生的事件。

    • 不会产生error事件
    • 一定在MainScheduler订阅(主线程订阅)
    • 一定在MainScheduler监听(主线程监听)
    • 共享状态变化

    Sequences - 序列

    有限观察序列 (Finite observable sequences)

    该序列是指那些最后会以completed或者error事件终止生命周期的可观察对象。
    比如网络任务:

    API.download(file: "http://www...")
        .subscribe( onNext: { data in
                            append data to temporary file }, 
                    onError: { error in
                            display error to user }, 
                    onCompleted: {
                            use downloaded file })
    
    无限观察序列 (Infinite observable sequences)

    与网络任务不同的是,UI以及交互事件都是无限观察序列。它们并不存在一个明确的生命周期终止。
    比如:

    UIDevice.rx.orientation.subscribe(onNext: { current in 
        switch current { 
            case .landscape:
                re-arrange UI for landscape 
            case .portrait:
                re-arrange UI for portrait 
        } 
    })
    

    Observer - 观察者

    观察者是用来监听事件,然后它需要这个事件做出响应。

    AnyObserver

    可以用来描述任意一种观察者

    Binder
    • 不会处理错误事件
    • 确保绑定都是在给定Scheduler上执行(默认MainScheduler

    一旦产出错误事件,在调试环境下将执行fatalError,在发布环境下将打印错误信息。

    Operator - 操作符

    操作符可以帮助我们创建新的序列,或者变化组合原有的序列,从而生成一个新的序列。

    filter - 过滤
    map - 转换
    zip - 配对

    还有很多,这里暂时不展开太多。

    Disposable - 可被清除的资源

    通常来说,一个序列如果发出了error或者completed事件,那么所有内部资源都会被释放。如果需要提前释放或取消订阅,那么可以使用dispose方法。

    一般我们推荐使用清除包(DisposeBag)或者takeUntil操作符来管理订阅的生命周期。

    DisposeBag - 清除包
    var disposeBag = DisposeBag()
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
    
        textField.rx.text.orEmpty
            .subscribe(onNext: { text in print(text) })
            .disposed(by: self.disposeBag)
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
    
        self.disposeBag = DisposeBag()
    }
    

    清除包被释放的时候,清除包内部所有可被清除的资源(Disposable)都将被清除。

    takeUntil
    override func viewDidLoad() {
        super.viewDidLoad()
    
        ...
    
        _ = usernameValid
            .takeUntil(self.rx.deallocated)
            .bind(to: passwordOutlet.rx.isEnabled)
    
        _ = usernameValid
            .takeUntil(self.rx.deallocated)
            .bind(to: usernameValidOutlet.rx.isHidden)
    
        _ = passwordValid
            .takeUntil(self.rx.deallocated)
            .bind(to: passwordValidOutlet.rx.isHidden)
    
        _ = everythingValid
            .takeUntil(self.rx.deallocated)
            .bind(to: doSomethingOutlet.rx.isEnabled)
    
        _ = doSomethingOutlet.rx.tap
            .takeUntil(self.rx.deallocated)
            .subscribe(onNext: { [weak self] in self?.showAlert() })
    }
    

    这将使得订阅一直持续到控制器的dealloc事件产生为止。

    Schedulers - 调度器

    SchedulesRX实现多线程的核心模块,主要用于控制任务在哪个线程或队列运行。

    在线程这部分主要有两个操作符:observeOn 和 subscribeOn ,常用的还是 observeOn 。

    调用 observeOn 指定接下来的操作在哪个线程。
    调用 subscribeOn 决定订阅者的操作执行在哪个线程。

    let rxData: Observable<Data> = ...
    
    rxData
        .subscribeOn(ConcurrentDispatchQueueScheduler(qos: .userInitiated))
        .observeOn(MainScheduler.instance)
        .subscribe(onNext: { [weak self] data in
            self?.data = data
        })
        .disposed(by: disposeBag)
    
    使用subscribeOn

    subscribeOn来决定数据序列的构建函数在哪个Scheduler

    使用observeOn

    observeOn来决定在哪个Scheduler监听这个数据序列.

    一个比较典型的例子就是,在后台发起网络请求,然后解析数据,最后在主线程刷新页面。你就可以先用 subscribeOn 切到后台去发送请求并解析数据,最后用 observeOn 切换到主线程更新页面。

    MainScheduler

    主线程

    SerialDispatchQueueScheduler

    串行队列

    ConcurrentDispatchQueueScheduler

    并行队列

    OperationQueueScheduler

    可以理解成NSOperationQueue.

    Error Handling - 错误处理

    一旦序列里面发生了一个error事件,整个序列将被终止。

    • retry - 重试
    • catch - 恢复
    retry
    retryWhen

    延时后重试

    catchError

    catchError可以在错误产生时,用一个备用元素或者一组备用元素将错误替换掉。

    result

    可以将错误转换成result,即使发生了错误,序列也不会终止。

    Subject

    Subject是一个桥梁,既是 Observable 也是 Observer

    • 作为一个 Observer,它可以订阅序列
    • 同时作为一个Obserable,它可以转发或者发射数据。

    Subject有几种:

    • PublishSubject
    • ReplaySubject
    • BehaviorSubject
    • Variable
    PublishSubject

    当有观察者订阅PublishSubject时,PublishSubject会发射订阅之后的数据给这个观察者。(存在数据丢失的问题)

    ReplaySubject

    和PublishSubject不同,不论观察者什么时候订阅,ReplaySubject都会发射完整的数据给观察者。

    BehaviorSubject

    当一个观察者订阅一个BehaviorSubject,它会发送原序列最近的那个值(如果还没有值会有默认的),之后继续发射原序列的值。

    Variable

    Variable是BehaviorSubject的一个封装,但是不会因为错误终止也不会正常终止,是一个无限序列。

    注意的问题

    cell中使用button重复订阅的问题

    解决办法:为Cell添加一个DisposeBag,在prepareForReuse时,更换新的DisposeBag

    var disposeBag = DisposeBag()
    
    override func prepareForReuse() {
        super.prepareForReuse()
        disposeBag = DisposeBag()
    }
    

    冷、热信号的问题

    热信号,一般是我们接触到的最新的东西。
    冷信号,一般是依赖其他的信号订阅后的行为,比如一些异步操作。

    耦合性问题

    RXSwift滥用会导致项目耦合性变差,可读性和可维护性都会变得很差,并且有一定的学习成本,会给项目本身带来一定的风险。

    循环引用问题

    RXSwift使用过程中会大量使用block,使用不当容易导致循环引用。

    相关文章

      网友评论

          本文标题:RxSwift 入门以及了解

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