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 。对于这种统一了形式的代码,工程的开发和维护难度都会大大降低。