RXSwift基础用法及响应核心底层逻辑原理(超详细吐血篇)

RXSwift GitHub地址
RXSwift中文文档
RX家族

絮叨:
这篇之所以讲核心逻辑时很仔细(啰嗦),是因为其重要性,必须要给弄懂了!。

简介

全称ReactiveX for Swift,是一个简化异步编程的框架,实现了函数响应式编程,事件与对象紧密联系,业务流清晰,便于管理。在RxSwift中,所有异步操作(事件)和数据流均被抽象为可观察序列的概念。
流程为:
创建序列 -> 订阅序列 -> 发送信号 -> 信号接收

简单使用:

  • 框架导入: CocoaPods
pod 'RxSwift'
pod 'RxCocoa'
//友情提示 当使用  Moya-ObjectMapper 时,由于此库引用了RxSwift,所以如果有pod 'Moya-ObjectMapper/RxSwift'时,无需再pod 'RxSwift'
写为:
#pod 'RxSwift'
pod 'RxCocoa'
pod 'Moya-ObjectMapper/RxSwift'

在需要使用的类中添加依赖:

import RxCocoa
import RxSwift
  • 简单用法:
    1 : KVO
class LBPerson:NSObject{
    @objc dynamic var name : String = "default"
}

extension LBRegisterViewController{
    fileprivate func kvoTest(){
        let person = LBPerson.init()
        person.rx.observeWeakly(String.self, "name")
            .subscribe(onNext: { (value) in
            print(value as Any)
        })
        .disposed(by: disposeBag)
        
        person.name = "lb"
    }
}

输出结果:

Optional("lb")

2 : UIButton 事件响应

fileprivate func btnTest(){
    let btn = UIButton()
    btn.rx.tap.subscribe(onNext: { (_) in
        print("事件")
    })
}

/*注:tap默认为TouchUpInside. 点进去看源码*/
extension Reactive where Base : UIButton {

    /// Reactive wrapper for `TouchUpInside` control event.
    public var tap: RxCocoa.ControlEvent<Void> { get }
}

//那么其他类型Event呢: 你看-->
btn.rx.controlEvent(.touchUpOutside).subscribe(onNext: { (_) in
    print("事件")
})

输出:

"事件"

3: UITextFiled 键盘内容监听

fileprivate func TFTest(){
    let tf = UITextFiled()
    let textFieldInputSequence = tf.rx.text.orEmpty.asDriver().throttle(0.4)

    textFieldInputSequence.drive(onNext: { (str) in
            print("tf值改变-->\(str)")
    }).disposed(by: disposeBag)
}

// orEmpty,防空处理.
// throttle节气阀,防暴力触发,参数为时间间隔.

4: UIScrollView ContentOffser监听

let scrollView = UIScrollView()
scrollView.rx.contentOffset
   .subscribe(onNext: { [weak self](content) in
       print(scrollView.contentOffset.y)
   })
.disposed(by: disposeBag)

5: 手势监听

let tap = UITapGestureRecognizer()
let label = UILabel()
label.addGestureRecognizer(tap)
label.isUserInteractionEnabled = true
tap.rx.event.subscribe(onNext: { (tap) in
    print(tap.view)
})
.disposed(by: disposeBag)

6: 通知

NotificationCenter.default.rx.notification(UIResponder.keyboardWillShowNotification)
    .subscribe(onNext: { (noti) in
        print(noti)
    })
.disposed(by: disposeBag)

7: 定时器

let timer = Timer.init()
timer = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
timer.subscribe(onNext: { (num) in
    print(num)
}).disposed(by: disposeBag)

8: 网络(URLSession)

let url = URL(string: "https://www.baidu.com")
//        原本写法
//        URLSession.shared.dataTask(with: url!) { (data, response, error) in
//            print(String.init(data:data!,encoding: .utf8))
//        }.resume()
        
URLSession.shared.rx.response(request: URLRequest(url: url!))
   .subscribe(onNext: { (response,data) in
       print(response)
   }).disposed(by: disposeBag)

这个看起来有点鸡肋,后续文章中会给大家介绍RXSwift结合MOYA使用,从而更优雅的调用网络。先看个效果:

let provider = MoyaProvider<ApiManager>();

provider.request(.login(username: "haha", password: "123456", token: "***")) { (result) in
     if result.error == nil{
          LBLog(result.value);
     }
}

核心逻辑:

重点:重头戏它又来了.

先来个最简单的响应流程:

fileprivate func exploringTest(){
   let ob = Observable<Any>.create { (observer) -> Disposable in
       observer.onNext("测试")
       observer.onCompleted()
       return Disposables.create()
   }
        
   _ = ob.subscribe(onNext: { (text) in
       print("订阅到:\(text)")
   }, onError: { (error) in
       print("error:\(error)")
   }, onCompleted: {
       print("完成")
   }, onDisposed: {
       print("销毁")
   })
}

打印结果:

订阅到:测试
完成
销毁

以上便是一个序列,从创建,到订阅,发送,接收,销毁的全过程。
那么接下来我们来撩开它滴裙子👗

一步一步来,

  • 先来看Observable<Any>.create
    首先Cmd+点击Observable,进来了,找了一堆没找着.create,不急,看图
    Observable.create

    选择Create.Swift, 看到了啥,第一个就是ObservableType的create方法
Observable.create方法实现

小注: 有滴同学说我找的是Observablecreate方法,你找的这个不是ObservableType吗。
答: 方法查找流程。那么Observable点进去你看

ObservableType与Observable关系

好滴,找到了create实现。

总结:我们看到我们所写的Observable<Any>.create其实就是return了一个AnonymousObservable对象的构造方法,也就是返回了一个AnonymousObservable对象。这个对象中储存了我们发送的事件,需要回调的处理。

继续,本文件搜索AnonymousObservable,找到这个类

找到AnonymousObservable

找到构造方法具体实现了
我们看到它啥也没干,只是把传给自己的匿名函数用成员变量保存了下来。OK,Observable<Any>.create干了啥我们知道了。继续-->

  • ob.subscribe
    第二句代码ob.subscribe
    老规矩 先点进去再说,CMD+点subscribe
    ObservableType.subscribe

    我再点:额。方法实现不给看了。 无妨,既然你都告诉我了是ObservableTypesubscribe(onNext:balabala方法,那我自己找。还是看图:
    图666: ObservableType的subscribe(onNext:...方法实现

好我们来到了重点了:
首先Disposable放过不管
图中第一个绿框,创建了一个AnonymousObserver订阅者对象。又是构造方法,又不给点进去?,没关系,我们自己查:

AnonymousObserver构造方法

绿框框里看到啥,AnonymousObserver 的初始化构造方法时init(_ eventHandler: @escaping EventHandler),利用逃逸闭包写法,当逃逸闭包被当作函数参数初始化时,可以把闭包写开,这个gay位同学应该问题不大。

总结:
第一个绿框中干了啥?
一句话:创建了一个AnonymousObserver对象,并且在创建时就保存了传进来的eventHandler的Block到自己的成员变量中。

ok,继续看图666中,AnonymousObserver创建后,第二个绿框,return的时候调用了self.asObservable().subscribe(observer)方法 (Disposables不用管,后续我会专门写一篇文章来介绍垃圾袋机制).

注意:注意:坑来了啊,self.asObservable().subscribe(observer),提问:想看subscribe的实现,那我们改找哪个类。
Observable? 错啦。仔细回忆下第一句代码,Observable<Any>.create时做了什么,上面第一句代码总价加粗的字体:

因为第一句代码返回了一个AnonymousObservable对象。所以此时self指的是AnonymousObservable对象,而asObservable()则是return了一个self,啥也没干,因此我们需要去找AnonymousObservable的subscribe()方法

不信?看图,


self.asObservable().subscribe(observer)中,self指的是谁

走到这 ,恭喜,裙底的风光快显露了。
继续,找AnonymousObservable的subscribe()方法,同样在Create.swift文件中.

AnonymousObservable对象

what the hell?为什么对象里没得subscribe()方法。 别急,子类找不着找谁?找爸爸。CMD+点Producer

Producer调用subscribe()方法

图上看到,不管 if / else 都会创建SinkDisposer类,然后调用self.run方法.
注意: 别忘了,儿子AnonymousObservable重写了run方法,儿子没得方法才找到的爸爸Producer
因此回到AnonymousObservable中,

终于被我看见裙底了。


AnonymousObservable对象,run方法

创建了AnonymousObservableSink对象,调用其run方法,

run

run干了啥?调用传参中parent的Block,传进来的parent是谁?

执行谁的_subscribeHandler

传进来是self,那self的_subscribeHandler是谁?
ok 第一句代码create时我们保存的,也就是说,

总结:图666中第二个绿框(也就是self.asObservable().subscribe(observer))干了啥.
一: 保存了接收到事件时的回调.
二: 执行我们创建序列时的回调.

换句话说,此时接下来就走到了序列创建回调中。


序列创建回调

那么接下来我们查找 AnonymousObserveron() 方法

AnonymousObserver类

没找着on(Next)方法?找父类 ObserverBase
ObserverBase类以及on(_ event: Event<E>)方法

方法里使用了个 switch,最后都调用了self.onCore(event)方法,由于子类AnonymousObserver重写了onCore方法所以回到子类调用(不信你看 大大的override写着),最终找到执行保存的_eventHandler,参数为调用onNext时传递的参数,也就是我们写的字符串"测试",也就是我们创建订阅者时传进来的收到订阅回调。
以及onCore方法

至此:从创建序列 -> 订阅序列 -> 发送信号 -> 信号接收整个流程我们已经分析完毕。(我都觉得自己啰嗦的不行,但是没办法,谁让是重点,老老实实吃透吧)。

最后流程总结:画图(改图为引用自Cooci老师)


响应流程核心原理完整流程

写这篇博客花了3个多小时,学习是枯燥的过程,但是坚持下来的时候是最爽的。

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