转载自: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. HDU 1242 Rescue(BFS),ZOJ 1649

    题目链接 ZOJ链接 Problem Description Angel was caught by the MOLIGPY! He was put in prison by Moligpy. The ...

  2. 分享到QQ空间、新浪微博、腾讯微博和人人网

    function shareys(type, url, title, img, content) { switch (type) { case "sina": url = &quo ...

  3. Arch安装fcitx输入法

    安装fcitx,安装gtk.qt模块. [root@ARCH ~]# pacman -S fcitx-im :: There are 4 members in group fcitx-im: :: R ...

  4. chapter 12_1 数据文件

    Lua的一个重要特性,就是可以作为配置文件,利用到table构造式来定义一种文件格式. 只需要在写数据时做一点额外的工作,读取数据就会变得相当容易.也就是将数据作为Lua代码输出. 当运行这些代码时, ...

  5. HTML转PDF

    1)使用工具转换.如:wkhtmltopdf 2)Chrome浏览器打印功能中,有另存为PDF格式文件.(推荐最便捷)

  6. VMware 下的Linux系统远程连接putty

    ifconfig查看ip地址 虚拟网卡需要自己新建  nat8 putty不能显示中文的解决办法 http://jingyan.baidu.com/article/5552ef47df8a97518f ...

  7. Cash Machine

    Problem Description A Bank plans to install a machine for cash withdrawal. The machine is able to de ...

  8. display flex 和a标签不行

    父元素display: flex;  display: -webkit-flex; flex-flow: row wrap; -webkit-flex-flow: row wrap; 配合子元素 fl ...

  9. iOS 计算两个坐标之间的距离

    //第一个坐标 CLLocation *before=[[CLLocation alloc] initWithLatitude:29.553968 longitude:106.538872]; //第 ...

  10. WebDriver获取table的内容(通过动态获取Table单元格的TagName对其innerHTML值进行获取)

    import java.util.ArrayList;import java.util.Iterator;import java.util.LinkedHashMap;import java.util ...