【iOS 开发】NSError ** 与 throws 的三个问题

简介: 问题一:为什么有错误处理还要返回值?NSFileManager 里面有这样一个方法:- (BOOL)removeItemAtURL:(NSURL *)URL error:(NSError **)error;使用的时候我们会传入一个 &error...

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

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


参考链接:

目录
相关文章
|
1月前
|
API 数据安全/隐私保护 iOS开发
利用uni-app 开发的iOS app 发布到App Store全流程
利用uni-app 开发的iOS app 发布到App Store全流程
88 3
|
3月前
|
存储 iOS开发
iOS 开发,如何进行应用的本地化(Localization)?
iOS 开发,如何进行应用的本地化(Localization)?
122 2
|
3月前
|
存储 数据建模 数据库
IOS开发数据存储:什么是 UserDefaults?有哪些替代方案?
IOS开发数据存储:什么是 UserDefaults?有哪些替代方案?
39 0
|
3月前
|
安全 编译器 Swift
IOS开发基础知识: 对比 Swift 和 Objective-C 的优缺点。
IOS开发基础知识: 对比 Swift 和 Objective-C 的优缺点。
93 2
|
3月前
|
API 开发工具 iOS开发
iOS 开发高效率工具包:10 大必备工具
iOS 开发高效率工具包:10 大必备工具
48 1
|
3月前
|
API 数据安全/隐私保护 iOS开发
利用uni-app 开发的iOS app 发布到App Store全流程
利用uni-app 开发的iOS app 发布到App Store全流程
54 1
|
9天前
|
API 定位技术 iOS开发
IOS开发基础知识:什么是 Cocoa Touch?它在 iOS 开发中的作用是什么?
【4月更文挑战第18天】**Cocoa Touch** 是iOS和Mac OS X应用的核心框架,包含面向对象库、运行时系统和触摸优化工具。它提供Mac验证的开发模式,强调触控接口和性能,涵盖3D图形、音频、网络及设备访问API,如相机和GPS。是构建高效iOS应用的基础,对开发者至关重要。
12 0
|
24天前
|
开发工具 Swift iOS开发
利用SwiftUI构建动态用户界面:iOS开发新范式
【4月更文挑战第3天】 随着苹果不断推进其软件开发工具的边界,SwiftUI作为一种新兴的编程框架,已经逐渐成为iOS开发者的新宠。不同于传统的UIKit,SwiftUI通过声明式语法和强大的功能组合,为创建动态且响应式的用户界面提供了一种更加简洁高效的方式。本文将深入探讨如何利用SwiftUI技术构建具有高度自定义能力和响应性的用户界面,并展示其在现代iOS应用开发中的优势和潜力。
|
2月前
|
监控 API Swift
用Swift开发iOS平台上的上网行为管理监控软件
在当今数字化时代,随着智能手机的普及,人们对于网络的依赖日益增加。然而,对于一些特定场景,如家庭、学校或者企业,对于iOS设备上的网络行为进行管理和监控显得尤为重要。为了满足这一需求,我们可以利用Swift语言开发一款iOS平台上的上网行为管理监控软件。
200 2
|
3月前
|
数据可视化 iOS开发
iOS 开发,什么是 Interface Builder(IB)?如何使用 IB 构建用户界面?
iOS 开发,什么是 Interface Builder(IB)?如何使用 IB 构建用户界面?
40 4