在 Swift 中使用 Objective-C 风格的异步 API
许多 Objective-C 风格的异步 API 会在它们的回调闭包中传入两个可选类型值:一个代表操作成功时方法的返回值,另一个代表操作失败时返回的错误值。 一个例子是 Core Location 框架中的 CLGeocoder.reverseGeocodeLocation 方法。它接受一个 [CLLocation]() 对象,然后将坐标信息发送到 Web 服务器,服务器会将坐标解析为可读的地址。当网络请求完成时,该方法会调用回调闭包,参数为一个存储 CLPlacemark 对象的可选数组以及一个可选型的 Error 对象: class CLGeocoder { ... func reverseGeocodeLocation(_ location: CLLocation,completionHandler: @escaping ([CLPlacemark]?,Error?) -> Void) ... } 在 Objective-C 风格的 API 中,返回一对可选型的成功值和错误的模式是处理这种情况时最实用的方案。 两个可能的结果,四个潜在的状态当前 API 的问题是,操作实际上只有两种可能:请求成功并返回结果,或者失败并返回错误。然而,这段代码却允许四种不同的状态:
API 的文档可以明确排除最后两种情况,但作为用户,你永远都不能真正确保文档是正确的。 使用 Result 实现更优的设计在 Swift 中你可能像这样设计同样的 API: class CLGeocoder { ... func reverseGeocode(location: CLLocation,completion: @escaping (Result<[CLPlacemark]>) -> Void) ... } 现在回调闭包中只接受一个(非可选型)参数,它的类型为 enum Result<T> { case success(T) case failure(Error) }
使用这个虚构的新 API,编译器可以保证传递给回调闭包的参数只能有两个状态,即成功或失败。你不必担心两个值都存在或都不存在的情形。 一个把 (T?,Error?) 转换成 Result<T> 的构造器然而我们不能修改苹果的 API,所以对回调闭包中参数固有的模糊性无能为力。我们能做的是包含一个将可选的成功值和可选错误转换为单个 import Foundation // needed for NSError extension Result { ///通过一个可选型的成功值与一个可选型的错误值 ///初始化一个 Result 对象。 /// 以便把苹果的异步 API 返回的值转换为一个 Result。 init(value: T?,error: Error?) { switch (value,error) { case (let v?,_): // 如果值是非空的忽略错误 self = .success(v) case (nil,let e?): self = .failure(e) case (nil,nil): let error = NSError(domain: "ResultErrorDomain",code: 1,userInfo: [NSLocalizedDescriptionKey: "Invalid input: value and error were both nil."]) self = .failure(error) } } } 当两个输入都为 let location = ... let geocoder = CLGeocoder() geocoder.reverseGeocodeLocation(location) { placemarks,error in // 把参数转换为 Result let result = Result(value: placemarks,error: error) // 只对这里的 result 做操作 switch result { case .success(let p): ... case .failure(let e): ... } } 使用了额外的一行代码,将参数转换为一个 2017 年 1 月 20 日的更新:Shawn Throop 建议优化我之前所述的 extension CLGeocoder { func reverseGeocode(location: CLLocation,completion: @escaping (Result<[CLPlacemark]>) -> Void) { reverseGeocodeLocation(location) { placemarks,error in completion(Result(value: placemarks,error: error)) } } }
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |