NSError ** 与 throws 的三个问题

问题一:为什么有错误处理还要返回值?

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 中哪个值可以保证你的操作是成功的。


参考链接:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值