RxSwift学习一

响应式编程(Reactive Programming,简称RP),是一种编程范式,于1997年提出,可以简化异步编程,提供更优雅的数据绑定。一般与函数式融合在一起,所以也会叫做:函数响应式编程(Functional Reactive Programming,简称FRP)

比较著名的、成熟的响应式框架:

ReactiveCocoa
ReactiveX

一、 RxSwif

Podfile

use_frameworks!
target 'YOUR_TARGET_NAME’ do
pod 'RxSwift', ‘6.2.0’
pod 'RxCocoa', ‘6.2.0’
end

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

2、RxSwif的核心角色:

  • Observable(可监听序列):产生事件,负责发送事件(Event)
  • Observer(观察者):响应事件,负责订阅Observable,监听Observable发送的事件(Event)
2.1、Observable

所有的事物都是序列
之前我们提到,Observable 可以用于描述元素异步产生的序列。这样我们生活中许多事物都可以通过它来表示,例如:

  • Observable<Double> 温度**

    你可以将温度看作是一个序列,然后监测这个温度值,最后对这个值做出响应。例如:当室温高于 33 度时,打开空调降温。

  • Observable<OnePieceEpisode>

    你也可以把《海贼王》的动漫看作是一个序列。然后当《海贼王》更新一集时,我们就立即观看这一集。

  • Observable<JSON> JSON
    你可以把网络请求的返回的 JSON 看作是一个序列。然后当取到 JSON 时,将它打印出来。

  • Observable<Void> 任务回调
    你可以把任务回调看作是一个序列。当任务结束后,提示用户任务已完成。

如何创建序列

现在我们已经可以把生活中的许多事物看作是一个序列了。那么我们要怎么创建这些序列呢?

实际上,框架已经帮我们创建好了许多常用的序列。例如:button的点击,textField的当前文本,switch的开关状态,slider的当前数值等等。

另外,有一些自定义的序列是需要我们自己创建的。这里介绍一下创建序列最基本的方法,例如,我们创建一个 [1,2,3] 的序列


import RxSwift
import RxCocoa
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let observable = Observable<String>.create { (observe:AnyObserver) in
            observe.onNext("1")
            observe.onNext("2")
            observe.onNext("3")
            observe.onCompleted()
          return  Disposables.create()
        }

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

创建序列最直接的方法就是调用 Observable.create,然后在构建函数里面描述元素的产生过程。 observer.onNext(0) 就代表产生了一个元素,他的值是 0。后面又产生了 9 个元素分别是 1, 2, ... 8, 9 。最后,用 observer.onCompleted() 表示元素已经全部产生,没有更多元素了。

public enum Event<Element> {
case next(Element)
case error(Swift.Error)
case completed
}

  • next - 序列产生了一个新的元素
  • error - 创建序列时产生了一个错误,导致序列终止
  • completed - 序列的所有元素都已经成功产生,整个序列已经完成
   let  observable = Observable.just("10")
   let  observable = Observable.of("1","2","3”)
   let  observable = Observable.from(["1","2","3"])
  • 其中Observable.just("10")等价于 observe.onNext("10”) observe.onCompleted()
  • Observable.of("1","2","3”)和Observable.from(["1","2","3”])则等价于 observe.onNext("1”) observe.onNext("2")
    observe.onNext("3")
    observe.onCompleted()

我们可以用以上学习的方式来封装功能组件,例如,闭包回调:

typealias JSON = Any

let json: Observable<JSON> = Observable.create { (observer) -> Disposable in

    let task = URLSession.shared.dataTask(with: ...) { data, _, error in

        guard error == nil else {
            observer.onError(error!)
            return
        }

        guard let data = data,
            let jsonObject = try? JSONSerialization.jsonObject(with: data, options: .mutableLeaves)
            else {
            observer.onError(DataError.cantParseJSON)
            return
        }

        observer.onNext(jsonObject)
        observer.onCompleted()
    }

    task.resume()

    return Disposables.create { task.cancel() }
}

json
    .subscribe(onNext: { json in
        print("取得 json 成功: \(json)")
    }, onError: { error in
        print("取得 json 失败 Error: \(error.localizedDescription)")
    }, onCompleted: {
        print("取得 json 任务成功完成")
    })
    .disposed(by: disposeBag)
2.2、Observer

在上面我们虽然只学习如何创建一个Observable,但是在上面其实我们已经接触到了Observer。回忆一下上面对Observable的监听是这样的。

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

创建观察者最直接的方法就是在 Observable 的 subscribe 方法后面描述,事件发生时,需要如何做出响应。而观察者就是由后面的 onNext,onError,onCompleted的这些闭包构建出来的。

   observable.subscribe(onNext: { data in
            print(data)
     }, onError: { error in
    print("发生错误: \(error.localizedDescription)")
}, onCompleted: {
    print("任务完成")
})

那么如何自己去创建一个Observer对象呢?

import RxSwift
import RxCocoa
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let observer = AnyObserver<Int>.init { event in
            switch event {
            case .next(let data):print(data)
            case .completed:print("completed")
            case .error(let error):
                print(error)
            }
        }
        Observable.just(1).subscribe(observer)
    }
}
  • AnyObserver 可以用来描叙任意一种观察者。
Binder

import RxSwift
import RxCocoa
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
      
        let observer:Binder = Binder(self) { (target,age:Int)  in
            print(target)
        }
        Observable.just(5).bind(to: observer)
    }
}
  • Binder也是一个observer
  • Binder主要有以下两个特征:
    1、不会处理错误事件
    2、确保绑定都是在给定Scheduler上执行(默认 MainScheduler)

import RxSwift
import RxCocoa
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let lable = UILabel()
        self.view .addSubview(lable)
        lable.backgroundColor = .red
        lable.frame = CGRect(x: 100, y: 100, width: 100, height: 40)
        let binder  = Binder(lable) {(lable, str:String) in
            lable.text = str
        }
        Observable.just("test").bind(to:binder)
    }
}
  • 这里的observer是一个UI观察者,只会处理 next 事件,并且更新 UI 的操作需要在主线程上执行,因此建议使用 Binder是更好的方案。

Disposable

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

  • dispose():立即取消订阅
  • disposed(by bag: DisposeBag):当bag销毁时,会自动调用Disposable实例的dispose。

import RxSwift
import RxCocoa
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let lable = UILabel()
        let bag = DisposeBag()
        self.view .addSubview(lable)
        lable.backgroundColor = .red
        lable.frame = CGRect(x: 100, y: 100, width: 100, height: 40)
        let binder  = Binder(lable) {(lable, str:String) in
            lable.text = str
        }
        Observable.just("test").bind(to:binder).disposed(by: bag)
    }
}

在这里bag作为控制器的成员变量,只有ViewController销毁时,bag才会被销毁,也只有这时才会被取消订阅。

  • 使用清除包(DisposeBag),我们就可以让ARC 来管理订阅的生命周期了。

2.3、补充

框架已经帮我们创建好了许多常用的序列。例如:button的点击,textField的当前文本,switch的开关状态,slider的当前数值等等。

     
        let btn = UIButton()
        let textFiled = UITextField()
        let slider = UISlider()
        let switchs = UISwitch()
    
        btn.rx.tap.subscribe { event in}.dispose()

        textFiled.rx.text.subscribe { event in}.dispose()
        
        slider.rx.value.subscribe { event in}.dispose()
        
        switchs.rx.isOn.subscribe { event in}.dispose()
      

在我们所遇到的事物中,有一部分非常特别。它们既是可监听序列也是观察者。
例如:textField的当前文本。它可以看成是由用户输入,而产生的一个文本序列。也可以是由外部文本序列,来控制当前显示内容的观察者:

// 作为可监听序列
let observable = textField.rx.text
observable.subscribe(onNext: { text in show(text: text) })

// 作为观察者
let observer = textField.rx.text
let text: Observable<String?> = ...
text.bind(to: observer)

有许多 UI 控件都存在这种特性,例如:switch的开关状态,segmentedControl的选中索引号,datePicker的选中日期等等。

扩展Binder属性

extension Reactive where Base: UIControl {
    var hidden: Binder<Bool> {
        Binder<Bool>(base) { button, value in
            button.isHidden = value
        }
    }

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

推荐阅读更多精彩内容