iOS 下的响应式编程

本节简单讨论一下在 iOS 下,Rx 为此做了哪些处理。

在 iOS 中,状态更新是很麻烦的事情,因为通知其他对象的方案有很多:

  • Target-Action
  • Delegate
  • KVO
  • Notifications
  • Callback

多种回调方式,适用规则、适用场景都不相同,这增加了开发、维护的难度(尤其体现在使用了全部的回调方式的工程)。Rx 在这里做了统一。

Target-Action

Target-Action 通常用在 Button 点击、手势触发等场景,调用一个 addTarget 即可:

public func addTarget(target: AnyObject?, action: Selector, forControlEvents controlEvents: UIControlEvents)

比如 Button 点击就是这个样子:

override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton()
button.addTarget(self, action: #selector(DownloadImageWithRxViewController.buttonTap(_:)), forControlEvents: .TouchUpInside) // 1
}
// ...
private dynamic func buttonTap(sender: UIButton) { // 2
// ...
}

Target-Action 模式存在的一个缺陷就是增加了阅读代码难度,一个 Button 点击事件的添加在 1 处,然而点击的逻辑却在 2 处。

Rx 将这种 Target-Action 转换成了流的概念,即转换成了一个 Observable ,使用起来就好了很多:

button
.rx_controlEvent(.TouchUpInside)
.subscribeNext {
// ...
}
.addDisposableTo(disposeBag)
此外,因为 Button 的点击属于很常用的一种方法,Rx 提供了一个便捷的属性:rx_tap 。

这样的代码就很容易将谁的点击事件点击事件的逻辑放在了一起。

Delegate

以点击 UITableView 中的 Cell 为例,使用 delegate 的代码大概如下:

tableView.delegate = self
// ...
extension ViewController: UITableViewDelegate {
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// ...
}
}

首先和 Target-Action 一样,增加了代码阅读难度,此外同一个实例中处理多个 UITableView ( UITableViewDelegate ) 将是一件很复杂的事情。Rx 同样将其转成了 Observable :

tableView
.rx_itemSelected
.subscribeNext { indexPath in
// ...
}
.addDisposableTo(disposeBag)

优势和上面的 点击 Button 一样,降低了阅读代码的难度。

KVO (Key-Value Observing)

使用 KVO 是件相当麻烦的事情:

class MyClass: NSObject {
dynamic var date = NSDate()
}
private var myContext = 0
class Class: NSObject {
var myObject: MyClass!
override init() {
super.init()
myObject = MyClass()
myObject.addObserver(self,
forKeyPath: "date",
options: .New,
context: &myContext)
}
override func observeValueForKeyPath(keyPath: String?,
ofObject object: AnyObject?,
change: [String : AnyObject]?,
context: UnsafeMutablePointer<Void>)
{
if let change = change where context == &myContext {
let a = change[NSKeyValueChangeNewKey]
print("日期发生变化 \(a)")
}
}
deinit {
myObject.removeObserver(self, forKeyPath: "date")
}
}

事情变得如此复杂,判断是否为想要观察的值移除观察者

在 Rx 中完成这件事非常轻松,当然,Rx 也将 KVO 转换成了 Observable :

myObject
.rx_observe(NSDate.self, "date")
.subscribeNext { date in
// ...
}
.addDisposableTo(disposeBag)

调用 rx_observe ,传入类型和 path 即可。

Notifications

Notifications 也被转换成了 Observable :

NSNotificationCenter.defaultCenter()
.rx_notification(UIKeyboardDidShowNotification)
.subscribeNext { notification in
// ...
}
.addDisposableTo(disposeBag)

Callback

在上一节已经有实现的代码,用 Alamofire 做网络请求,将回调结果转换成 Observable :

Observable<NSData>.create { (observer) in
let downloadImage = request(.GET, "https://github.com/fluidicon.png")
.responseData { (reponse) in
switch reponse.result {
case .Success(let data):
observer.onNext(data)
case .Failure(let error):
observer.onError(error)
}
observer.onCompleted()
}
return AnonymousDisposable {
downloadImage.cancel()
}
}

总结

Rx 统一了 iOS 开发中复杂繁多的回调方案,统一转换成了 Observable 。对于这种统一了形式的代码,工程的开发和维护难度都会大大降低。