Iterable 和 Observable
在上一节简单的讨论了一点异步相关的问题,本节将继续讨论一些异步的事情。
上一节中同步下载图片的代码:
let imageURL = NSURL(string: "https://github.com/fluidicon.png")!
let data = NSData(contentsOfURL: imageURL)!
let image = UIImage(data: data)
self.imageView.image = image
笔者在这里尝试改成链式的写法,大体如下:
let image = NSURL(string: "https://github.com/fluidicon.png")
.map { NSData(contentsOfURL: $0)! }
.map { UIImage(data: $0)! }
imageView.image = image
更有意思一点的写法是使用 flatMap :
let image = NSURL(string: "https://github.com/fluidicon.png")
.flatMap { NSData(contentsOfURL: $0) }
.flatMap { UIImage(data: $0) }
imageView.image = image
这样的写法,通过点访问的形式对数据进行迭代,一步一步得到预期的结果。相比之前的代码,这样以迭代形式写的代码有两个优势:
更清晰的代码结构
通过一个 map 或者 flatMap ,可以清晰的表述 map 或者 flatMap 中传入的闭包只是一个步骤的处理过程,这个闭包的实现与上下文没有依赖的关系。
回到开始的处理方案,在这四行代码中,我们很难直接理解每一步的处理过程是什么。
let imageURL = NSURL(string: "https://github.com/fluidicon.png")!
// ------
let data = NSData(contentsOfURL: imageURL)!
let image = UIImage(data: data)
// ------
self.imageView.image = image
是这样吗?分三步?
还是这样的分三步:
let imageURL = NSURL(string: "https://github.com/fluidicon.png")!
let data = NSData(contentsOfURL: imageURL)!
// ------
let image = UIImage(data: data)
//------
self.imageView.image = image
或者说,他们本身就是四个小的步骤。
用了 map 或者 flatMap 就不一样的:
let image = NSURL(string: "https://github.com/fluidicon.png")
// ------
.flatMap { NSData(contentsOfURL: $0) }
// ------
.flatMap { UIImage(data: $0) }
很明显,获取图片的过程是三步。
显然,map 和 flatMap 很好的为代码进行了分层,使代码更加清晰易读。
有意思的是,为 Optional 增加一个方法,可以使代码变得更加 Iterable :
NSURL(string: "https://github.com/fluidicon.png")
.flatMap { NSData(contentsOfURL: $0) }
.flatMap { UIImage(data: $0) }
.forValue { [unowned self] in self.imageView.image = $0 }
forValue 的实现如下:
protocol Setable { }
extension Setable {
func forValue(body: Self -> Void) {
body(self)
}
}
extension Optional: Setable { }更清晰的描述
前面的代码还可以写的更有意思些:
NSURL(string: "https://github.com/fluidicon.png")
.flatMap(download) // 下载
.flatMap(parseToImage) // 解析成 UIImage
.forValue(setImageTo(imageView)) // 设置 UIImage 到 UIImageView
相比之前的代码,这更加明确了,除了将步骤分的更清晰,还为每一个步骤都定义了个名字。具体上面三个的实现如下:
let download: NSURL -> NSData? = { url in
return NSData(contentsOfURL: url)
}
let parseToImage: NSData -> UIImage? = { data in
return UIImage(data: data)
}
func setImageTo(_ imageView: UIImageView) -> UIImage? -> Void {
return { image in
imageView.image = image
}
}可以注意到,对于同步操作的迭代,Iterable 可以很轻松的完成需求,那么异步的迭代如何完成?比如使用 Alamofire 做网络请求:
Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"])
.responseJSON { response in
if let JSON = response.result.value {
print("JSON: \(JSON)")
// 在这里 return 吗?
}
}

这根本就无法完成。
更强大的 Iterable : Observable
首先 Iterable 可以完成的事情 Observable 一样可以完成。Observable 是 Iterable++ ,它还可以完成像上面的 Alamofire 的异步回调迭代。
来看一个将 Alamofire 回调闭包转换成 Observable 的过程:
Observable<NSData>
.create { observer in
let downloadImage = request(.GET, "https://github.com/fluidicon.png")
.responseData { (response) in
if let data = response.data {
observer.onNext(data) // 这里调用 onNext 就类似于调用 return
}
observer.onCompleted()
}
return AnonymousDisposable {
downloadImage.cancel()
}
}
整段代码目前最需要关注的点是 observer.onNext(data) 类似于迭代过程中调用 return data 将值传递下去,将 return 换成 onNext 传递迭代后的值,从而可以在这种异步回调的代码中继续优雅的迭代下去。
.map { return "https://github.com/fluidicon.png" }
.map { rawURLString in return NSURL(string: rawURLString)! }
.map { url in return NSURLRequest(URL: url) }
.flatMap { urlRequest -> Observable<NSData> in
return Observable<NSData>
.create { observer in
let downloadImage = request(.GET, urlRequest)
.responseData { (response) in
if let data = response.data {
observer.onNext(data)
}
observer.onCompleted()
}
return AnonymousDisposable {
downloadImage.cancel()
}
}
}
.map { data in UIImage(data: data)! }总结
Iterable 和 Observable 都可以更好的组织代码,让代码描述的逻辑更加清晰,此外 Observable 是更强大的 Iterable ,具有处理异步的能力。