内容简介:RxCocoa是RxCocoa里面也定义了很多类,专门为UI处理提供的,比如ControlProperty、比如前几天在一篇博客上,看到的一段代码:
RxCocoa是 RxSwift 的一部分,主要是UI相关的Rx封装。比如实现了很多组件的绑定功能,可以把值跟控件之间相互绑定,可以避免写很多通知、修改数据等代码。也可以监听delegate改变,无须把控件创建及delegate处理分开写等。
RxCocoa里面也定义了很多类,专门为UI处理提供的,比如ControlProperty、 ControlEvent 、 Driver 、Binder等。RxCocoa可以用好的话,可以极大简化UI相关处理逻辑。但是,要想随心所欲的使用,还是要对其实现要有一定的了解,否则就容易写出不是那么简洁的代码。
比如前几天在一篇博客上,看到的一段代码:
textfield.rx.text .asObservable() .subscribe { print($0) } .disposed(by: disposeBag)
这段代码一眼看过去,是没什么问题的,执行起来看起来也是ok的。
但是,这段代码的问题就是存在冗余。具体为什么冗余,稍后再分析。先从简单的示例了解RxCocoa
简单示例
RxSwift 的源码里面有附带示例代码,源码clone下来之后,打开Rx.xcworkspace,即可以选择示例运行看效果。
现在就拿最简单的Numbers例子看下,(也可以在线看下这个最简单示例代码: Numbers )
核心代码就这一段:
Observable.combineLatest(number1.rx.text.orEmpty, number2.rx.text.orEmpty, number3.rx.text.orEmpty) { textValue1, textValue2, textValue3 -> Int in return (Int(textValue1) ?? 0) + (Int(textValue2) ?? 0) + (Int(textValue3) ?? 0) } .map { $0.description } .bind(to: result.rx.text) .disposed(by: disposeBag)
这个示例是将三个输入框的内容加起来,绑定在Label上,上面值变化之后,下面的Label立即跟着变化。
为了让例子更简单,我们可以只把label的值绑定在第一个输入框上:
number1.rx.text.orEmpty .map { $0.description } .bind(to: result.rx.text) .disposed(by: disposeBag)
现在我们逐个分析下,这里面的 rx、text、orEmpty、bind到底是什么
rx
number1.rx是表示什么呢?我们根据源码来推导一下
先看下rx属性的源码实现:
public var rx: Reactive<Self> { get { return Reactive(self) } set { // this enables using Reactive to "mutate" base object } }
实际上是创建了一个Reactive对象,所以:
number1.rx ===> Reactive(number1)
Reactive源码又如下:
public struct Reactive<Base> { /// Base object to extend. public let base: Base /// Creates extensions with base object. /// /// - parameter base: Base object. public init(_ base: Base) { self.base = base } }
所以number1.rx最终会变成如下代码:
public struct Reactive { public let base: UITextField // 指向number1 }
所以number1.rx是变成了 Reactive结构体 ,此时,Reactive的扩展方法,我们就可以使用了。
所以,这个.rx是进入Rx世界的入口,控件调用.rx属性之后,后面的内容就表示进入了Rx的世界了。
text
上面分析了,number1.rx是一个Reactive的结构体,它后面就可以继续调用Reactive及其扩展的属性和方法了。
(实际上number1.rx只能调用不限定的扩展的方法或者限定Base是UITextField类型的扩展方法)
所以,text属性就是Reactive的扩展的属性。这个属性定义在UITextField+Rx.swift文件中,下面是其简化后的实现:
extension Reactive where Base: UITextField { public var text: ControlProperty<String?> { return value } public var value: ControlProperty<String?> { // 默认事件支持了allEditingEvents、valueChanged return base.rx.controlPropertyWithDefaultEvents( getter: { textField in // 要发出事件时,所需的数据,即是从这儿获取 textField.text }, setter: { textField, value in // 作为监听者时,收到数据时会调到这儿 if textField.text != value { textField.text = value } } ) } }
text简单的调用了value属性,它们是ControlProperty类型的。
ControlProperty是表示控件属性,即能监听变化,又能发出通知,即同时实现了ObservableType和ObserverType协议,所以控件才能支持 双向绑定 。
上面代码中的getter块是在作为ObservableType时所使用的,setter块是作为ObserverType所需要的。
具体ControlProperty实现就不展开了,可以在UIControl+Rx.swift查看具体实现。
orEmpty
上面代码中的一个细节:
public var text: ControlProperty<String?>
这个value值传出来之后是String?的,是个可选值。在很多情况下,并不想要可选值,只希望如果为nil时,传””空字符串出来即可。否则可选值出来之后,外面可能处理为nil的情况处理起来会比较繁琐。
orEmpty就是这个作用。
orEmpty的实现如下:
public var orEmpty: ControlProperty<String> { let original: ControlProperty<String?> = self.asControlProperty() let values: Observable<String> = original._values.map { $0 ?? "" } let valueSink: AnyObserver<String> = original._valueSink.mapObserver { $0 } return ControlProperty<String>(values: values, valueSink: valueSink) }
会根据目前的values构造一个新的values,并最终构造一个新的ControlProperty,去除可选值的情况。
bind
bind即是用来将一个信号发送者和一个信号监听者绑定在一起,即有信号发送,监听者自动收到通知。听起来是跟subscribe做的事情比较类似。
bind(to:)有几个不同的实现,最简单的实现版本如下:
public func bind<O: ObserverType>(to observer: O) -> Disposable where O.E == E { return self.subscribe(observer) }
其实就是订阅的封装。可以理解为为subscribe起了一个在绑定场景下比较容易理解的名字。
在很多情况下,我们的observer并不需要处理error、complete事件,并且处理逻辑需要在主线程中执行。所以RxCocoa帮我们封装好了一个叫Binder的对象,我们使用这个对象时,不需要考虑太多。
RxCocoa提供的一些监听属性,比如UILabel的rx.text属性即是Binder类型的:
extension Reactive where Base: UILabel { public var text: Binder<String?> { return Binder(self.base) { label, text in label.text = text // 收到事件就会调用到这儿 } } }
结尾
这样这个简单示例就串起来了,并且能够很明确每次调用在做什么事情。
现在再看最开始的那段代码:
textfield.rx.text .asObservable() .subscribe { print($0) } .disposed(by: disposeBag)
我们知道textfield.rx.text是ControlProperty的类型,并且这个类型本身就实现了Observable协议的,那么冗余的代码就很明确了,即asObservable()这一次调用是没意义的。
以上所述就是小编给大家介绍的《一个RxCocoa简单示例及其实现原理》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Spring事务用法示例与实现原理
- Apache Struts OGNL注入漏洞原理与示例
- Arduino+Avr libc制作Badusb原理及示例讲解
- 粒子滤波Matlab示例
- transformers示例
- 粒子滤波Matlab示例
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Python神经网络编程
[英]塔里克·拉希德(Tariq Rashid) / 林赐 / 人民邮电出版社 / 2018-4 / 69.00元
神经网络是一种模拟人脑的神经网络,以期能够实现类人工智能的机器学习 技术。 本书揭示神经网络背后的概念,并介绍如何通过Python实现神经网络。全书 分为3章和两个附录。第1章介绍了神经网络中所用到的数学思想。第2章介绍使 用Python实现神经网络,识别手写数字,并测试神经网络的性能。第3章带领读 者进一步了解简单的神经网络,观察已受训练的神经网络内部,尝试进一步改......一起来看看 《Python神经网络编程》 这本书的介绍吧!