为啥使用 NSError 间接参数而不是 @try/@catch/@finally

Posted

技术标签:

【中文标题】为啥使用 NSError 间接参数而不是 @try/@catch/@finally【英文标题】:Why use NSError indrect parameters instead of @try/@catch/@finally为什么使用 NSError 间接参数而不是 @try/@catch/@finally 【发布时间】:2018-02-01 03:01:44 【问题描述】:

从其他问题,我可以看到建议NSError用于可恢复的错误,@throw/@catch/@finallyNSException应该用于致命错误。

这对我来说毫无意义。为什么要使用NSException 来处理致命错误? 关键是他们可以被抓到!如果抓到他们不是重点,那为什么还有@try/@catch系统呢?为什么不只是NSLogexit(1),然后就结束了?

NSError 也很笨拙,这让我更喜欢@throw/@catch/@finally

是什么激励使用其中一个而不是另一个?

【问题讨论】:

你在内部做什么是你的事。但是 NSException 并不那么容易被捕获。假设你的代码是从 Swift 中使用的。 Swift 程序员可以捕获你的 NSError 但不能捕获你的 NSException。 @matt “没那么容易被抓住。”为什么?是的,我知道 Swift 不支持 NSException,这对我来说也毫无意义。我最好的猜测是 NSError 已经是主要的错误处理机制,所以没有必要实现对在 swift 中捕获 NSException 的支持。但这并不能解释为什么 NSError 在 Swift 之前占主导地位。 假设 Cocoa 抛出一个 NSException。通常发生的不是你抓住它;而是你抓住了它。通常发生的事情是你崩溃了。但是当 Cocoa 给你一个 NSError 时,它会向你发送一条消息,解释为什么它不能返回请求的值。这些是 Cocoa 设计模式。 那么 NSError 占主导地位是因为它更容易被忽略? @Alexander 它可以被忽略的事实是语言规则的副产品,而不是框架的约定(例如,如果 Obj-C 像 Swift 那样有错误规则,你就不会可以忽略它们)。也可以在 Matt 的回答中查看我的 cmets — 这里的主要区别在于,一个是针对 programmer 错误,另一个是针对正常运行时的“预期”错误。 【参考方案1】:

这些是长期存在的 Cocoa 设计模式。

从您对值的请求(例如“请尝试从该字符串创建 URL”)以良好的顺序(通常通过间接方式)返回 NSError 对象。当无法提供该值时,它会被有效地替换,实际上是一个精心制作的消息,一个可能包含您可以向用户展示的文本的信息包,关于用户如何从这种情况中恢复的建议等等。

发生 NSException 是因为 程序员 犯了严重错误。它代表了一个致命的问题,因此它渗透到链条上并使程序突然结束。 it 包含的消息仅适用于程序员

【讨论】:

这正是它的要点。 NSException 表示 programmer 错误;他们可以被抓住,但不是故意的。并非 Cocoa 框架的每个部分都支持从抛出的异常中恢复,并且许多部分都被假定为结束程序执行。如果没有special flag,ARC 也不会尝试在异常处理代码附近重新排序retains 和releases,以确保在实际捕获到异常时不会泄漏对象(因为假设是无论如何,这个过程很快就会结束)。 所有可恢复的错误,然后,通过NSError,其 MO 是最终用户可呈现性。错误可能很严重,但如果不是由于程序员错误,应该以某种方式处理并呈现给用户(这就是为什么NSErrors 被本地化到这样的程度)。 @Alexander @catch 并不总是内置在语言中,而 IIRC,在NSException 切换到使用 C++ 异常机制之前,它们的捕获和抛出成本比现在高。但除了人体工程学之外,我认为这并没有真正深刻的技术原因——这主要是一种哲学选择。程序员错误相对较少,但运行时错误很常见,并且应该始终是用户可呈现的。通过引用返回错误很便宜,尤其是与异常相比(过去更是如此);其他一切都是围绕约定建立的。 目前很难找到这方面的好资料,但我相信 @try@catch 是在 ObjC 2.0 中引入的,那时,NSException 基础架构切换到使用C++ 异常机制。到目前为止,我认为NSException 是建立在setjmp/longjmp 之上的。这可能是也可能不是您正在寻找的技术推理...... 顺便说一句,“通常的模式”有一些例外(!)。例如,+[NSExpression expressionWithFormat:] 如果格式字符串格式不正确(在 Swift 中无法捕获,并且使 NSExpression 对于用户提供的输入几乎无用),则会引发异常。【参考方案2】:

苹果说:

重要提示:您应该保留将异常用于编程或意外的运行时错误,例如越界集合访问、尝试改变不可变对象、发送无效消息和断开连接到窗口服务器。您通常在创建应用程序时而不是在运行时处理这些类型的异常错误。

 

错误对象 (NSError) 和 Cocoa 错误传递机制是在 Cocoa 应用程序中传达预期错误的推荐方式,而不是异常。

来源:Exception Programming Topics

【讨论】:

查看我的问题的最后一行 @Alexander Apple 不会说明原因,也许是框架内部的某些东西。我从经验中知道:如果你不遵守苹果的规则,你就会遇到麻烦。这也是其他问题推荐NSError的原因。 现在 Apple 说“使用 Swift”和“错误处理是响应程序中的错误条件并从错误条件中恢复的过程。Swift 为抛出、捕获、传播和操作可恢复对象提供一流的支持运行时出错。”。也许将来框架会在 Swift 中,NSError 会消失。

以上是关于为啥使用 NSError 间接参数而不是 @try/@catch/@finally的主要内容,如果未能解决你的问题,请参考以下文章

为啥我们使用 .NET 属性而不是普通的旧 get/set 函数?

为啥使用 string_view 而不是广义的 container_view<T>?

为啥对函数的 VLA 数组参数使用星号“[*]”而不是整数?

为啥 WCF 会返回 myObject[] 而不是 List<T> 像我期望的那样?

为啥 BCL 集合使用结构枚举器,而不是类?

Objective-C 我应该使用 NSError 以及为啥 presentError 函数不起作用