Rx中的反应式编程
首先,我当然建议您阅读官方文档。 在那里,您可以找到RxSwift运算符的基础知识。
本质上,在RxSwift中,所有事物都是围绕可观察对象构造的。 根据其名称,您可能会猜测我们将在整个程序中观察到此类对象的变化。 特别是,这些对象将通知您有关其状态的更改。 此外,我们拥有执行任何必要动作的能力,这些动作将改变返回的对象(过滤器,地图,flatMap等)。
因此,完整的工作流程如下所示:使用某些功能创建一个可观察对象。 例如,一个内部包含单个参数并返回可观察对象的对象。 此外,还有大量其他变体。 您可以在文档中检查它们。
使用不同的运算符(如果状态已更改)描述其行为。 例如,为了在特定条件下获取结果而进行过滤,或者设置一个线程来运行结果代码。 为每个状态(下一步,完成,错误)设置处理程序(如果需要)。 订阅-从现在开始通知观察者我们希望获得其通知。
此代码说明了订阅某些可观察对象的示例。 魔鬼并不像他画的那么黑,所以不要害怕。 该示例非常简单,因此我们将尝试逐步解释它。 您可以在文档中轻松找到所有未知方法。 首先,我们将创建一个OperationQueue。 这个阶段对您来说并不难。 现在,让我们深入探讨其他复杂的问题。
BackgroundWorkScheduler描述了用于从队列的后台执行操作的线程。 下一部分很有趣。 有些对象可以上传视频,我们将订阅其更改。
应该在后台线程上进行工作的设置,我们将在该线程上处理初始结果。
- 使用“ map”方法转换对象以使其成为字符串。
- 在主线程中开始工作,以进行UI工作
- “设置”,处理下一个状态的功能。 实际上,我们在下面的几行中执行了所有这些操作。
让operationQueue = NSOperationQueue() operationQueue.maxConcurrentOperationCount = 3 operationQueue.qualityOfService = NSQualityOfService.UserInitiated 让backgroundWorkScheduler = OperationQueueScheduler(operationQueue:operationQueue) videoUpload .observeOn(backgroundWorkScheduler) .map({json in 返回json [“ videoUrl”]。stringValue }) .observeOn(MainScheduler.sharedInstance) .subscribeNext {网址 self.urlLabel.text =网址 }
此外,我们可以构建自己的可观察对象。 我们所需要的只是描述它的行为方式。
以下示例是关于对象创建的。 我们从Realm公司网站上的RxSwift教程中获取了它。 这里的主要思想是将请求组成一些服务器。 从我的角度来看,这是一个相当常见的任务。 幸运的是,我们可以显示其中的所有RX功率。
创建{(观察者:AnyObserver)->一次性 让request = MyAPI.get(URL,(((result,error)-> { 如果让err =错误{ rator.onError(err); } 否则,如果让authResponse =结果{ rator.onNext(authResponse); rator.onComplete(); } }) 返回AnonymousDisposable { request.cancel() } }
根据前面的段落,可以很容易地构思出此代码。 但是,让我们弄清楚它。
“创建”功能构造一个可观察对象,该对象将“ GET”请求发送到服务器。 在代码主体中,您会注意到三种常见的RX状态。 如果出现问题,我们将向观察者发送“错误”状态。 否则,如果得到响应,我们将分派“下一步”,表明我们已更改状态。 最后,我们发送“完成”,表示我们已完成工作。
最后但并非最不重要的是AnonymousDisposable。 我们使用一次性物品退订当前对象。 通过创建它们,我们描述了取消订阅后应执行的操作。 同样,在前面的示例中,当我们没有任何观察者时,我们很容易取消请求。 看起来和没有反应一样吗? 让我们检查一下这一小段代码可以带来什么好处。
我们通常必须取消相同的请求(如果存在),以避免重复并提高应用程序的速度。 在这种情况下,我们需要在请求完成后调用存储完成块。 通常,我们通过将块存储在数组中,然后将其存储在带键的字典中来执行此操作,这是唯一的并且与我们的请求有关。 看起来很难完成这么简单的任务。
我们实际上可以从RX中受益吗? 订阅我们新对象的任何对象都将被通知完成。 结果,不需要任何其他数据存储。 方便简单。 不够?
此外,让我们考虑另一个问题-“块中的数据”。 您可能已经注意到,我们在“下一步”状态之后立即发送“完成”。 原因很明显-我们收到了所有必要的数据(已经描述过)。 如果我们没有足够的信息,我们只发送“ Next”而不发送“ Completed”。 “完成”选项应与最后一条数据一起发送。 订户要做的就是在他获得“下一个”时收集数据,并在获得“已完成”的情况下开始使用它。 很简单,不是吗? 我想您可能会发现这段代码很简单,所以只需像在这些示例中一样进行一些修改即可。
在下一节中,我将演示一些RxSwift运算符。 如前所述,您可以在RxSwift文档中查看整个列表。
可观察的
将您的对象转换为可观察对象(前提是可以转换您的对象)。
设变量=变量(0) variable.asObservable()。subscribe {e in 打印(e) } variable.value = 1 下一个(0) 下一个(1)
创建
该运算符使用其自身的逻辑来启用可观察对象的完全创建。 我们在前面的示例中应用了它。
让firstSequence = Observable.of(1、2、3) 让secondSequence = Observable.of(“ A”,“ B”,“ C”) 让multipleSequence = Observable> .create { viewer.on(.Next(firstSequence)) viewer.on(.Next(secondSequence)) 返回NopDisposable.instance } 让concatSequence = multipleSequence.concat() concatSequence.subscribe {e in 打印(e) } 下一个(1) 下一个(2) 下一个(3) 下一个(A) 下一个(B) 下一个(C)
递延
有机会删除可观察对象创建过程的时间,直到订阅它为止。
var i = 1 let deferredJustObservable = Observable.deferred { Observable.just(i) } 我= 2 _ = deferredJustObservable.subscribeNext {打印(“ i = \($ 0)”)} 我= 2
上面列出的运营商是目前使用最广泛的。 需要明确的是,RxSwift还具有与不断变化的UI配合使用的类似物,例如Rxtext,Rxenabled等。RxSwift及其UI表示形式之间的唯一区别是它是可观察的类型,您可以订阅它们以进行检查这些属性何时会改变。
最后,我附上了以下示例,以向您展示如何在RxSwift中使用UI。 尽管我不会简单地描述它,但我将比较用RxSwift编写的代码和标准的Apple代码方法,以强调其好处。
以下程序从文本字段中添加三个数字,并将结果存储在标签中。
进口基金会 导入UIKit #if!RX_NO_MODULE 导入RxSwift 进口RxCocoa #万一 类NumbersViewController:ViewController { @IBOutlet弱var number1:UITextField! @IBOutlet弱var number2:UITextField! @IBOutlet弱var数字3:UITextField! @IBOutlet弱var结果:UILabel! 覆盖func viewDidLoad(){ super.viewDidLoad() Observable.combineLatest(number1.rx_text,number2.rx_text,number3.rx_text){textValue1,textValue2,textValue3-> Int in return(Int(textValue1)?? 0)+(Int(textValue2)?? 0)+(Int(textValue3)?? 0) } .map {$ 0.description} .bindTo(result.rx_text) .addDisposableTo(disposeBag) } }
下一个示例说明了标准方法。
进口基金会 导入UIKit 类RealNumbersViewController:ViewController { @IBOutlet弱var数字3:UITextField! { didSet { self.connectTextField(self.number3) } } @IBOutlet弱var number1:UITextField! { didSet { self.connectTextField(self.number1) } } @IBOutlet弱var number2:UITextField! { didSet { self.connectTextField(self.number2) } } @IBOutlet弱var结果:UILabel! 覆盖func viewDidLoad(){ self.calculateResultForTextField(number1) self.calculateResultForTextField(number2) self.calculateResultForTextField(number3) } func textFieldDidChange(textField:UITextField){ self.calculateResultForTextField(textField) } 私人功能connectTextField(textField:UITextField){ textField.addTarget(self,action:#selector(RealNumbersViewController.textFieldDidChange(_ :)),forControlEvents:UIControlEvents.EditingChanged) } 私有函数calculateResultForTextField(textField:UITextField){ // //让calculatedValue =(Int(number1.text ?? 0)?? 0)+(Int(number2.text ?? 0)?? 0)+(Int(number3.text ?? 0)?? 0); 让firstOperand =(Int(textField.text ??“ 0”)?? 0) 让secondOperand =(Int(self.result.text ??“ 0”)?? 0) self.result.text =(firstOperand + secondOperand).description } }
如您所见,编写的代码量存在巨大差异。 RX方法可以消除不必要的条件。 我什至尝试通过覆盖设置器来使用React,但是仍然不能比完整的Reactive样式代码更好。
下一个示例描述密码和用户名验证。 工作流程如下:如果用户名无效,则无法插入密码。 假设用户名正确,但密码输入错误,则不允许我们按下按钮。 只要用户名和密码有效,带有警告文本的标签就会消失。
导入UIKit 让minimumUsernameLength = 5 让minimalPasswordLength = 5 类RealSimpleValidationViewController:ViewController { @IBOutlet弱var usernameOutlet:UITextField! { didSet { self.usernameOutlet.addTarget(self,action:#selector(RealSimpleValidationViewController.usernameChanged(_ :)),forControlEvents:UIControlEvents.EditingChanged) } } @IBOutlet弱var usernameValidOutlet:UILabel! @IBOutlet弱var passwordOutlet:UITextField! { didSet { self.passwordOutlet.addTarget(self,action:#selector(RealSimpleValidationViewController.passwordChanged(_ :)),forControlEvents:UIControlEvents.EditingChanged) self.passwordOutlet.enabled = self.paswordIsValid() } } @IBOutlet弱var passwordValidOutlet:UILabel! @IBOutlet弱var doSomethingOutlet:UIButton! func usernameChanged(textField:UITextField){ self.passwordOutlet.enabled = self.usernameIsValid() self.usernameValidOutlet.hidden = self.usernameIsValid() self.doSomethingOutlet.enabled = self.usernameIsValid()&& self.paswordIsValid() } func passwordChanged(textField:UITextField){ self.passwordValidOutlet.hidden = self.paswordIsValid() self.doSomethingOutlet.enabled = self.usernameIsValid()&& self.paswordIsValid() } func paswordIsValid()-> Bool { 返回self.passwordOutlet.text?.characters.count> minimalPasswordLength } func usernameIsValid()-> Bool { 返回self.usernameOutlet.text?.characters.count> minimumUsernameLength } }
RxSwift示例
导入UIKit #if!RX_NO_MODULE 导入RxSwift 进口RxCocoa #万一 //让minimalUsernameLength = 5 //让minimalPasswordLength = 5 类SimpleValidationViewController:ViewController { @IBOutlet弱var usernameOutlet:UITextField! @IBOutlet弱var usernameValidOutlet:UILabel! @IBOutlet弱var passwordOutlet:UITextField! @IBOutlet弱var passwordValidOutlet:UILabel! @IBOutlet弱var doSomethingOutlet:UIButton! 覆盖func viewDidLoad(){ super.viewDidLoad() usernameValidOutlet.text =“用户名必须至少为\(minimumUsernameLength)个字符” passwordValidOutlet.text =“密码必须至少为\(minimalPasswordLength)个字符” 让usernameValid = usernameOutlet.rx_text .map {$ 0.characters.count> = minimumUsernameLength} .shareReplay(1)//没有此映射的每个绑定将执行一次,默认情况下rx是无状态的 让passwordValid = passwordOutlet.rx_text .map {$ 0.characters.count> = minimalPasswordLength} .shareReplay(1) 让everythingValid = Observable.combineLatest(usernameValid,passwordValid){$ 0 && $ 1} .shareReplay(1) usernameValid .bindTo(passwordOutlet.rx_enabled) .addDisposableTo(disposeBag) usernameValid .bindTo(usernameValidOutlet.rx_hidden) .addDisposableTo(disposeBag) passwordValid .bindTo(passwordValidOutlet.rx_hidden) .addDisposableTo(disposeBag) 一切有效 .bindTo(doSomethingOutlet.rx_enabled) .addDisposableTo(disposeBag) doSomethingOutlet.rx_tap .subscribeNext {self中的[weak self] ?. showAlert()} .addDisposableTo(disposeBag) } }
让我们再讨论一个示例并实现这两个目标:使用一些RX运算符以一种反应性的方式改进我们的思维,可以肯定的是,用RX库编写的不同语言的代码片段实际上是相似且易于阅读的。
想象一下,您已经完成了一个漫长的开发周期,所有测试和应用抛光都已经结束了-看起来您只需要向商店提交应用即可。 反过来,它需要完成三件事:
开发人员方面的新版本
发行说明
UI / UX部门的一些资产。
准备要提交的应用程序的人应安排所有3个部分。 目前,我们拥有所需的一切。 下一步意味着将问题翻译成反应性语言。 在应用程序进入商店之前,应考虑4个可观察到的因素:
— releaseObservable —被所有人观察到的; 对应用的发布感兴趣(例如,产品所有者)。 — appBuildObservable —一旦正确准备好构建,便生成构建对象; — releaseNotesObservable —发布发行说明; — appStoreAssetsObservable —发出商店所需的资产。
appBuildObservable更加有趣。 我们必须逐字测试每个构建,因为没有QA工程师,它就无法上线。 说到反应式语言,我们需要对可观察对象进行过滤,以避免产生不适当的结果。
最终,我们有不同的观测值。 如何与他们一起比赛以获得合格的成绩? 我建议使用zip运算符来组合appBuildObservable,releaseNotesObservable和appStoreAssetsObservable。 它通过指定的函数将多个可观测对象的发射合并在一起,并为每个组合生成单个项。
创建结果可观测值之后,我们需要订阅它并开始接收数据。 我们可以确定希望观察对象使用哪个线程来执行操作(subscribeOn),以及我们希望在哪个线程上接收结果(observeOn)。
而已! 收到可观察到的将产生新数据。 前面提到的3个组件(新建,发行说明和UI / UX资产)将在后台线程中完成。 结果将被发送到应用程序的主线程。
上面提供的代码段是用Swift和Java编写的。 我认为它们非常易于阅读,看起来非常相似。 唯一的差异是由这两种编程语言之间的自然差异引起的。
此外,我们可以编写一个可以用不同语言以相同方式读取的代码。 我们的例子很好地证明了这一点。 由于反应式编程不仅是一个库,而是一种编程范例,因此有一些可用于最流行的语言和平台的实现。
响应式编程使我们首先考虑组织数据流,然后考虑如何编码。 此外,反应式库在不同语言的实现中为我们提供了相同的API和实体。 RxSwift中的Observable,Observer,Scheduler的含义与RxJava和RxAndroid中的Observable,Observer,Scheduler完全相同。 换句话说,如果我们为不同的平台开发相同的应用程序,以反应方式构建应用程序会带来很多好处。 我们可以为不同的操作系统构建相同的架构模型,并且仅使用本地方法编写应用程序的某些部分。
结论
在我看来,Rx代码显然是清晰,简单且易于管理的。 您实际上可以理解所有内容,而无需冗长的解释。
我们的结论是什么? 您应该回答以下问题。 您是否对现在使用反应性方法感兴趣,或者这不是您要找的东西?
对我而言,Reactive是构建时尚应用程序的一个新的强大方向。 我绝对推荐。
- Swift:forEach和forIn循环的实例方法之间的区别
- 如何覆盖/ swizzle在运行时的私人类的方法objective-c?
- iOS Safari通过单击button触发扫描信用卡
- 在CGAffineTransformMakeScale之后校正模糊文本
- iOS Dev:用于UITableView标签的NSArray返回'index 1 out of bounds'
- 在Swift数组下标索引中的错误?
- 无法通过iOS SDK和原生Facebook应用程序对Facebook进行身份validation
- iOS 5 UIAlertView自定义
- MonoTouch.Dialog:EntryElement的背景颜色