征服ReactiveSwift:简介
“反应式编程具有陡峭的学习曲线。”
在学习函数式反应式编程(FRP)时,我一直在听到这些。 从命令式编程的背景出发,我努力掌握了FRP的各种概念。 从最基本的角度来看,FRP的好处是它使您可以建模时间。 但这很难使您动脑子。 为了帮助您克服学习难题,我将在“征服ReactiveSwift”系列中分享我的学习经验。 我的目的是为初学者提供一个循序渐进的指南,以学习ReactiveSwift。
这是该系列的第一篇文章。 它介绍了功能性反应式编程,并说明了它与命令式编程的区别。
命令式与功能式反应
为了理解这些编程范例之间的区别,让我们举一个例子。 假设我们要实现一个用户界面,该用户界面具有一个UILabel (我们称之为label
)和一个UITextView (我们称之为textView
),其中UILabel反映了在UITextView中输入的文本。 为了实现这种行为,我们编写如下代码:
func textViewDidChange(_ textView: UITextView) {
label.text = textView.text
}
上面的代码可以正常工作。 我们一直在这样做。 那是什么问题呢? 让我们调查一下。 考虑一下旨在更新标签文本的语句:
label.text = textView.text
这是一个赋值语句。
这是什么意思? 这意味着在label.text
时, label.text
等于label.text
。 该语句不封装有关分配点之前或之后的label.text
状态的任何信息。 换句话说,一旦将时间概念引入模型,语句label.text is equal to textView.text
不一定成立。
因此,我们需要将此语句封装在委托方法textViewDidChange
,以使label
的状态保持一致。 以命令方式,很难优雅地表示这样的关系。
欢迎使用反应式绑定
在反应式编程中(特别是在ReactiveSwift中),可以通过绑定解决相同的问题。
label.reactive.text <~ textView.reactive.continuousTextValues
该语句暗示在label
的整个生命周期内, label
的文本与textView
的文本相等。
你有没有发现新的东西? <〜称为binding operator
。 运算符的左侧称为binding target
,右侧称为binding source
。 我们将探索“征服ReactiveSwift”系列中的后续文章。
为了可视化命令式和反应式编程的工作方式的差异,让我们考虑键入“ hello”一词。
在命令式方法中,我们根据可变状态对系统进行建模。 例如,在上图中,我们关心的是在输入每条额外的文本时保持label
的状态。
在FRP中,我们对系统响应事件流的行为进行建模。 在上面的示例中,我们定义了响应文本流的label
行为。 我们不关心为响应孤立事件(即每个单独的击键)而保持状态。
反应性方法还有另一个好处。 对于反应式代码,标签的行为在声明时是明确的。 而在命令式方法中,逻辑分散在各种委托方法中。 从长远来看,很难理解元素的行为,从而增加了引入错误的机会。
一些有用的定义
Heinrich Apfelmus([a] Haskell函数程序员)用两个语句总结了上面讨论的概念。
从[a]语义的角度来看,FRP就是要用时变函数而不是可变状态来描述系统。
按照语法标准,FRP就是在声明时完全指定值的动态行为。
本系列的下一篇…
希望本文使您有动力开始使用FRP。 您可以在此处找到示例代码。 在下一篇文章中,我们将讨论ReactiveSwift的不同原语。