专栏首页KyXu【iOS 开发】NSError ** 与 throws 的三个问题

【iOS 开发】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 条评论
登录 后参与评论

相关文章

  • 【iOS UI】iOS 9 GUI 资源分享

    分享的内容包括一个【DesignCode-iOS-9-GUI】Sketch 文件, 和苹果官方释出的【SF-UI、SF-Compact】两种字体的安装包。 ...

    KyXu
  • 【面试】实习求职终结篇

    在和腾讯、阿里各通了4次电话之后,翘掉了腾讯的最后一面(因为是在隔壁的城市,而且是群面,心理上有点抗拒),拿到了阿里无线事业部的 offer,个人还是很满意,反...

    KyXu
  • 【iOS 开发】Controller 之间使用代理传值

    控制器之间经常需要互相传递值,第一个控制器(简称 MasterVC)在通过 NavigationController Push 第二个控制器(简称 Detail...

    KyXu
  • iOS WebView内联播放视频无声音

    用户4458175
  • Python之easy_install安装出错

    forrestlin
  • 中国台湾大学林轩田机器学习基石课程学习笔记8 -- Noise and Error

    上一节课,我们主要介绍了VC Dimension的概念。如果Hypotheses set的VC Dimension是有限的,且有足够多N的资料,同时能够找到一个...

    红色石头
  • 爬虫训练之--获取错误并将其保存进本地文件

    明天依旧可好
  • 给企业微信加个群机器人

    现在很多企业在使用企业微信或钉钉进行工作交流,我们可以在群里添加一个自定义群机器人,定时发送一些提醒或咨询信息,它可以作为一个小组手,也为工作增加一点乐趣。

    NanBox
  • 关于一次线上出错的思考--如何规避线上程序崩盘

    在日常开发中,我们往往会有很多不好的习惯,写出一些非常不健壮的代码,导致由于数据和条件的多样性,程序未能作出很好的预判。同时由于我们未能对错误进行好的处理,导致...

    前端博客 : alili.tech
  • Vue如何在父级下使用v-slot的值

    https://cn.vuejs.org/v2/guide/components-slots.html#%E4%BD%9C%E7%94%A8%E5%9F%9F%...

    javascript.shop

扫码关注云+社区

领取腾讯云代金券