转载自:http://fengdeng.github.io/blog/2016/01/22/rxswift-dao-di-%5B%3F%5D-ge-uitextfieldshi-ru-he-he-%5B%3F%5D-ge-uilabelbang-ding-de/

多看多揣摩吧!

只要一行代码

writeTextField.rx_text.bindTo(displayLabel.rx_text)

看下效果

那么这到底是如何实现的呢

整体分析

代码层:
writeTextField的rx_text通过bingTo这个函数和displayLabel的rx_text绑定了。 Rx思想:
一个被观察者(Observable)通过bingTo函数和一个观察者(Observer)绑定了,这样无论被观察这如何变化,观察者都了如指掌

深层解析

writeTextField.rx_text

这是一个Observable。也是一个Observer。 rx_text是RxSwift给UITextField的一个拓展属性,上代码

public var rx_text: ControlProperty<String> {
return rx_value(getter: { [weak self] in
self?.text ?? ""
}, setter: { [weak self] value in
self?.text = value
})
}

rx_text是一个ControlProperty<String>。由后面的函数生成。我们去看下那个函数

func rx_value<T: Equatable>(getter getter: () -> T, setter: T -> Void) -> ControlProperty<T> {
let source: Observable<T> = Observable.create { [weak self] observer in
guard let control = self else {
observer.on(.Completed)
return NopDisposable.instance
} observer.on(.Next(getter())) let controlTarget = ControlTarget(control: control, controlEvents: [.AllEditingEvents, .ValueChanged]) { control in
observer.on(.Next(getter()))
} return AnonymousDisposable {
controlTarget.dispose()
}
}
.distinctUntilChanged()
.takeUntil(rx_deallocated) return ControlProperty<T>(values: source, valueSink: AnyObserver { event in
MainScheduler.ensureExecutingOnScheduler() switch event {
case .Next(let value):
setter(value)
case .Error(let error):
bindingErrorToInterface(error)
break
case .Completed:
break
}
})
}

这个函数是UIControl的,所有UIControl的所有子类都有哈。UITextField是UIControl的子类

函数有点长,我们一步一步分析,其实就两句

先说下第一句,source的生成过程。source是一个Observable,即这里的Observable

  1. 首先获取self,获取不到直接completed
  2. 获取到了,直接Next一个刚刚传进来的getter。这里是self?.text ?? "",即流走一个Observable
  3. 生成一个ControlTarget,这个ControlTarget每当ValueChanged都会流走一个Observable(这个ControlTarget有点多,下回讲)
  4. 上面三步走完后 ,这里的是一个Observable。对这个Observable进行distinctUntilChanged(),这个函数说明,只有String改变才会继续往下流
  5. takeUntil(rx_deallocated),直到这个UITextField被释放
  6. source结束,source的这个流程就是上面的5步

其实如果一个UITextField的rx_text作为一个Observable,上面的代码已经足够了。

为了实现UITextField也可以作为observer,于是有了下面的代码。

再说第二句,通过source,生成一个ControlProperty<String>。这个被观察者才是本函数要返回的类型。它既是一个Observer,也是一个Observable

  1. 直接调用ControlProperty<PropertyType>的初始化方法生成
  2. 看一下初始化方法带进去的一个闭包

     AnyObserver { event in
    MainScheduler.ensureExecutingOnScheduler() switch event {
    case .Next(let value):
    setter(value)
    case .Error(let error):
    bindingErrorToInterface(error)
    break
    case .Completed:
    break
    }

因为UI操作都必须在主线程。MainScheduler.ensureExecutingOnScheduler()这一句话是判断当前线程是不是在主线程 
下面的代码是当UITextField作为一个observer时,接收到一个流的响应。

displayLabel.rx_text

这是一个Observer rx_text是RxSwift给UILabel的一个拓展属性,上代码

public var rx_text: AnyObserver<String> {
return AnyObserver { [weak self] event in
MainScheduler.ensureExecutingOnScheduler() switch event {
case .Next(let value):
self?.text = value
case .Error(let error):
bindingErrorToInterface(error)
break
case .Completed:
break
}
}
}

很简单,只有响应流的代码。有流过来,就self?.text = value

有同学可能有疑问了,UITextField可以被观察,也可以是观察者。UILabel只能是观察者,我要是想观察UILabel的text怎么办呢???

哈哈,easy,please try KVO

displayLabel.rx_observe(String.self, "text").subscribe { (event) -> Void in
switch event{
case .Next(let m):
print(m)
case .Completed:
print("")
case .Error(let t):
print(t)
}
}

KVO还是比较简单的,大家可以自己看看。或许下一次我写写

bindTo

bingTo是Observable的拓展

public func bindTo<O: ObserverType where O.E == E>(observer: O) -> Disposable {
return self.subscribe(observer)
}

内部调用的Observable的subscribe方法来和一个observer进行关联

这里就是调用了ControlProperty的subscribe方法.

public func subscribe<O : ObserverType where O.E == E>(observer: O) -> Disposable {
return _values.subscribe(observer)
}

_values.subscribe(observer)调用了SubscribeOn这个类的subscribe方法,

SubscribeOn没实现subscribe方法,于是去他的父类Producer里去调用了

override func subscribe<O : ObserverType where O.E == Element>(observer: O) -> Disposable {
if !CurrentThreadScheduler.isScheduleRequired {
return run(observer)
}
else {
return CurrentThreadScheduler.instance.schedule(()) { _ in
return self.run(observer)
}
}
}

Producer又去它的子类SubscribeOn去调用了run方法

override func run<O : ObserverType where O.E == Ob.E>(observer: O) -> Disposable {
let sink = SubscribeOnSink(parent: self, observer: observer)
sink.disposable = sink.run()
return sink
}

当UITextField的每一个值产生时,调用了下面这个方法,在SubscribeOnSink这个类中

func on(event: Event<Element>) {
forwardOn(event) if event.isStopEvent {
self.dispose()
}
}

forwardOn(event)这个方法又去SubscribeOnSink这个类的父类中去调用了

final func forwardOn(event: Event<O.E>) {
if disposed {
return
}
_observer.on(event)
}

最终调到了_observer.on(event)这一句话。也是就UILabel的rx_text的on方法,on(event)是一个闭包,在UILabel的rx_text中等同以下代码

    { [weak self] event in
MainScheduler.ensureExecutingOnScheduler() switch event {
case .Next(let value):
self?.text = value
case .Error(let error):
bindingErrorToInterface(error)
break
case .Completed:
break
}

以上是一个复杂的继承

UITextField和一个UILabel绑定 浅析的更多相关文章

  1. Javascript 事件对象(四)一个事件绑定多个不同的函数

    给一个对象绑定多个事件处理函数: <!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-T ...

  2. 配置Nginx支持SSL SNI(一个IP绑定多个证书) 以及Haproxy实现多域名证书

    概述 传统的每个SSL证书签发,每个证书都需要独立ip,假如你编译openssl和nginx时候开启TLS SNI (Server Name Identification) 支持,这样你可以安装多个S ...

  3. 封装一个UILabel圆形边框显示进度

    封装了一个UILabel并让它显示圆形的边框,UILabel上面显示百份比,而边框则用Animation绘制到整个圆占指定百分比的点. 这只是我个人想的继承一个UILabel实现的,用到两个CASha ...

  4. APACHE如何里一个站点绑定多个域名?用ServerAlias

    APACHE2如何里一个站点绑定多个域名?用ServerAlias以前很笨,要使多个域名指向同一站点总是这样写: <VirtualHost *:80>ServerAdmin i@kuigg ...

  5. APACHE如何里一个站点绑定多个域名?用ServerAlias servername

    APACHE2如何里一个站点绑定多个域名?用ServerAlias以前很笨,要使多个域名指向同一站点总是这样写: <VirtualHost *:80>ServerAdmin i@kuigg ...

  6. u3d一个GameObject绑定两个AudioSource

    u3d 一个GameObject绑定两个AudioSource  ,使他们分别播放,并控制 using UnityEngine; using System.Collections; public cl ...

  7. APACHE如何一个站点绑定多个域名?

    大家肯定遇到过这样的情况,需要APACHE2里一个站点绑定多个域名,那么如何操作呢?用ServerAlias 以前很笨,要使多个域名指向同一站点总是这样写: ServerAdmin admin@dom ...

  8. 用ES6的class模仿Vue写一个双向绑定

    原文地址:用ES6的class模仿Vue写一个双向绑定 点击在线尝试一下 最终效果如下: 构造器(constructor) 构造一个TinyVue对象,包含基本的el,data,methods cla ...

  9. APACHE如何里一个站点绑定多个域名?用ServerAlias 转

    APACHE2如何里一个站点绑定多个域名?用ServerAlias以前很笨,要使多个域名指向同一站点总是这样写:<VirtualHost *:80>ServerAdmin i@kuigg. ...

随机推荐

  1. Unity3D 回合制 网上源码 目前还在研究构思

    我们已将回合制的战斗模式讲解得很清楚了.那么,如果在Unity3D游戏中实现一个回合制游戏呢?我们从最简单的一对一模式来设计回合制游戏的原型.我们可以游戏的状态划分为下面三种状态:        1. ...

  2. 10个男孩和n个女孩共买了n2+8n+2本书,已知他们每人买的书本的数量是相同的,且女孩人数多于南海人数,问女孩人数是多少?(整除原理1.1.3)

    10个男孩和n个女孩共买了n2+8n+2本书,已知他们每人买的书本的数量是相同的,且女孩人数多于南海人数,问女孩人数是多少? 解: 因为,每个人买的书本的数量是相同的, 所以,10|n2+8n+2 所 ...

  3. 安装vb6 正在更新系统 无响应

    新装的win10系统,安装vb6时,最后一直卡在“正在更新系统”,程序无响应,没办法,kill掉后,貌似不影响软件使用,但是安装vs6sp6B无法成功安装. 解决办法是: 不安装“数据访问”组件. 参 ...

  4. NSURL访问项目中的文件

    最近在研究视频处理,具体为:将一个mp4文件,拖入项目工程中,通过url访问文件. 开始代码如下: NSString *path = [[NSBundle mainBundle]pathForReso ...

  5. C#获取键盘和鼠标操作的时间的类

    /// /// 创建结构体用于返回捕获时间 /// [StructLayout(LayoutKind.Sequential)] struct LASTINPUTINFO { /// /// 设置结构体 ...

  6. php+ajax的三级联动下拉菜单

    封装一个三级联动,就可以在任何页面进行引用了 先写个页面引用一下这个js <head> <meta http-equiv="Content-Type" conte ...

  7. 解题的小问题(C++)

    1.判断一个数是否为整数  if(m==(int)m) 2.#include <bits/stdc++.h>using namespace std;int main(){    int n ...

  8. Robot Framework开发系统关键字详细

    本文的目的,是记录如何在rf下,开发属于自己的库函数(又称之系统关键字) 1.首先在..\Python27\Lib\site-packages目录下创建自定义库目录如Verification_Libr ...

  9. Chapter 2 Open Book——7

    I gunned my deafening engine to life, ignoring the heads that turned inmy direction, and backed care ...

  10. Django:之不得不说的web框架们

    python的web框架 Bottle Bpttle是一个快速.简洁.轻量级的基于WSIG的微型web框架,此框架只有一个.py文件,除了python的标准库外,其不依赖任何其它模块. pip ins ...