NSError ** 和抛出的三个问题
问题一:为什么有错误处理还要返回值?
NSFileManager
里面有这样一个方法:
- (BOOL)removeItemAtURL:(NSURL *)URL error:(NSError **)error;
使用的时候我们会传入一个 &error
再获取这个错误值,来看这个过程中有没有什么错误,那么通过 error == nil
不就可以知道是否执行成功吗,为什么需要 BOOL
返回值,这是一个冗余的设计吗?
考虑下面这种情况:
NSData *data = nil;
NSError *error = nil;
BOOL success = [data writeToURL:nil options:NSDataWritingAtomic error:&error];
我们会发现,由于 data 是 nil,这个方法会直接返回 0,但是 error 依然是 nil,所以官方文档也要求我们一定要通过返回值判断是否执行成功,而不是仅仅去对 error 判空。
另外,基于 Objective-C 的语言特性,这里我们无法阻止调用者对 error 参数传递 nil,但是这个方法在这种情况下依然需要告知调用者是否执行成功,所以返回值是一个必要的设计。
然而,下面我们会发现,虽然这不是一个冗余设计,但是这也不是一个好的设计。
问题二:如何做出一个没有返回值的错误处理?
上面那个方法在 Swift 中是这样的:
func removeItem(atPath path: String) throws
没有返回值
Objective-C 中为了对外部创建的 NSError 赋值,使用了双指针设计,即 NSError *__autoreleasing*,这种做法在 Swift 语言中,变成了 inout 关键字:
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
这实现了在函数中修改参数值,按照这种写法,是不是我们可以臆想出一种完全对应于 Objective-C 风格的版本:
func removeItem(atPath path: String) throws // 原版
func removeItem(atPath path: String, error: inout NSError) -> Bool // 臆想版本
理论上或许可行,但是这里我臆想出的这个版本,和 OC 中这个方法的设计,都是不好的设计:为了方便,很多时候开发者会对 error 传入
nil,这使得一旦出错,这里的 Error Handling 是无效的,而当初这里
传入 nil 也正是因为开发者认为这种同步方法不像异步的网络请求那样容易出错,最终就是艰难的 bug 排查。
Swift 2 引入的异常机制强迫我们使用下面的这种做法,
let fileManager = FileManager.default
do {
try fileManager.removeItem(atPath: filePath)
} catch {
print(error)
}
这样使得错误更加容易被发现和处理,并且由于 Swift 是强类型语言,在这里 nil 并不能执行 removeItem 方法,所以在这里,没有返回值却成了合理的设计。
但有一点需要注意,在这里我们只能获取到一个 error,我们却无法知道可以获取到一个什么样的 error,我们无法直接通过 API 知道,假如这里 removeItem 不成功,到底可能是因为什么样的原因而导致不成功。
问题三:throws 是同步的,异步的时候怎么办?
答:向 Error?
低头。
func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask
error An error object that indicates why the request failed, or nil if the request was successful.
由于 try catch
是一种同步的语法,在异步的时候,我们还是只能通过 Error 或者 NSError 来判断执行是否成功。
一种更好的做法其实是封装枚举,像这样:
enum JSONError: Error {
case noSuchKey(String)
case typeMismatch
}
对于这种做法可以参考 antitypical/Result,而如果你一定要使用原生 API,记得看一眼文档吧,到底 return value、error、responseData 中哪个值可以保证你的操作是成功的。
参考链接:
- developer.apple.com/library/con…
- developer.apple.com/library/con…
- onevcat.com/2016/03/swi…
- swifter.tips/error-handl…
上一篇: 本周预售新书
下一篇: 基于协同过滤算法的东北特产销售系统的设计
推荐阅读
-
最值得投资的服务O2O公司TOP10- http://www.chinaz.com/start/2014/1010/370056.shtml 创哥说:自2013年下半年开始,本地生活服务业里出现了众多O2O模式的创业公司。这是资本和舆论密切关注的热点,它们大多相信自己就是那个能改造本地生活服务业的人,资本和舆论差不多也这么想,所以它们创立没多久,就纷纷拿到了天使投资。现在大半年过去,它们要融A轮甚至B轮了。那么,哪些公司的发展前景更值得期待? 《创业家》跟踪了这波浪潮,并对其中的代表公司进行了深度采写。此外,《创业家》还通过微信群和“重度垂直-黑马O2O特训营”、线下沙龙等服务聚集起了中国最领先的O2O创业公司创始人,请加微信号korchagin,加群请注明姓名+公司名+职位,否则初审都不会过。 《创业家》采访了近10位投资人和大约30家从事本地生活服务的创业公司,试图找到每个细分行业里最优秀的早期公司。我们的评判标准与VC投早期项目时类似,主要看行业前景、团队结构、商业思路这三个维度。为了加强专业度和调查力度,我们还请投资人和业内公司作了互相评价。 有的人可能会想:我作为同行,明明三项指标都比上榜的公司好,为啥我榜上无名?因为很多概念,拆细了看才有价值。比如“行业前景”,创业者第一步切入的细分市场的规模与它所能延伸出的想象空间,其实是两回事。出行市场广阔无边,滴滴打车和易到用车虽然正在商务租车领域展开竞争,但它们最早切入的细分领域不同,所以发展节奏和今日的市值也就不同。又比如,二次创业者要比初次创业者有优势,因为阅历很值钱。但如果前者第一次创业时实现了财务*,那他二次创业时很可能饥饿感不够,这可能会抵销掉他的阅历优势。 NO.
-
iOS获取MCC和MNC的方法及替代方案:iOS 16中CTCarrier废弃问题重新解决
-
带权图上的三个问题--最短路问题、中国邮递员问题与货郎担问题
-
探究iptables和ipvs之间的规格冲突问题
-
LINQ:对列表和字典的处理效率测试——遇到的问题与解决方法
-
解决黑群晖的各种问题:修复黑裙洗白、黑裙休眠和硬盘顺序
-
解决Abaqus切削中的切屑问题:避免切槽加工中的缠绕和断切的技巧
-
搞定Oracle和MySQL的兼容性问题:LISTAGG与GROUP_CONCAT总结
-
在Oracle中使用list_oracle和listagg拼接字符串出现过长问题的解决方案
-
在前端学习中,使用iframe和hash做菜单导航时遇到的src跳转和contentWindow.location.replace()的问题解析