Objective-C:断言 vs. 异常 vs. 错误
Posted
技术标签:
【中文标题】Objective-C:断言 vs. 异常 vs. 错误【英文标题】:Objective-C: Assertion vs. Exception vs. Error 【发布时间】:2011-02-15 21:16:37 【问题描述】:在 Cocoa 中,我应该什么时候使用 NSAssert、NSException、NSError?
这是我一直在想的:
NSAssert - 在创建用于程序员自身利益的任何客户端程序时,请仔细检查规则、约定、假设或前置条件和后置条件?
NSException - 在创建第三方库以供使用该库的其他程序员的利益时,以便他们在输入无效时立即知道?
NSError - 与外部系统交互以获取文件、数据库或 Web 服务等数据时不能保证给我结果?
【问题讨论】:
【参考方案1】:NSAssert 在失败时会抛出异常。所以 NSAssert 有一种简短而简单的方法来编写和检查您在代码中所做的任何假设。它不是(在我看来)例外的替代方案,只是一条捷径。如果断言失败,那么您的代码中出现了严重错误,程序不应继续。
需要注意的一点是,NSAssert 不会在发布版本中编译到您的代码中,因此这通常用于开发期间的完整性检查。我实际上倾向于使用始终处于活动状态的自定义断言宏。
你会@throw
你自己的NSException的时候是你肯定希望它在发布版本中,以及在一些参数无效或你被错误调用的公共库/接口之类的东西中.请注意,@catch
异常并继续运行您的应用程序并不是真正的标准做法。如果您尝试使用一些 Apple 的标准库(例如 Core Data),可能会发生不好的事情。与断言类似,如果抛出异常,应用程序通常应该很快终止,因为这意味着某处存在编程错误。
NSErrors 应该在您的库/接口中用于不是编程错误并且可以从中恢复的错误。您可以向调用者提供信息/错误代码,他们可以干净地处理错误,在适当时提醒用户并继续执行。这通常用于文件未找到错误或其他一些非致命错误。
【讨论】:
更强烈的说,NSException不应该用来表示可恢复的错误。 换句话说:Obj-C 的 NSException == Java 的 Error 类,而 Obj-C 的 NSError == Java 的 Exception 类。万岁以保持一致! 实际上,如果您在预编译的前缀文件中不添加 NS_BLOCK_ASSERTIONS,则 NSAssert 将被编译到您的代码中。阅读下面的answer(现在只需获得 50 个推荐 :)【参考方案2】:Cocoa 中的约定是异常表示程序员错误。很多代码,包括框架代码,都没有被设计为在抛出异常后正常工作。
应该可以恢复的任何类型的错误都由NSError
表示。还有一个系统可以将NSError
s 呈现给用户。正如您所说,这对于容易出错的外部资源非常有用。
从概念上讲,断言是给定谓词始终评估为真的陈述;如果没有,则程序已损坏。虽然可以修改其行为,但 NSAssert
系列默认情况下是抛出 NSInternalInconsistencyException
s 的便捷方式(在发布版本中可以选择关闭它们)。
【讨论】:
【参考方案3】:编辑: 在 Xcode 4.2 中,assertions are turned off by default for release builds,
现在NSAssert 不会在发布构建中编译到您的代码中,但您可以在构建设置中更改它
@Mike Weller,你的回答有一个错误。
需要注意的一点是,NSAssert 不会在发布版本中编译到您的代码中,因此这通常用于开发期间的健全性检查。
实际上,如果您不在预编译的前缀文件中添加NS_BLOCK_ASSERTIONS
,NSAssert 将被编译到您的代码中。
在技术说明 TN2190 中,我们可以找到:
为您的预编译前缀文件指定诸如关闭 C 断言的 NDEBUG 或关闭 Foundation 的 NSAssert 的 NS_BLOCK_ASSERTIONS 之类的宏很重要
或者你可以阅读这个:How to know if NSAssert is disabled in release builds?
【讨论】:
【参考方案4】:一般来说,异常用于表示程序员的错误——它们是不应该发生的事情。错误用于表示程序正常运行中可能出现的错误条件——基本上是用户错误,或者需要为真但可能不是的外部条件。因此,尝试删除文档中的某些锁定元素可能会出错,而尝试在没有 Internet 连接的情况下下载文件会出错,但尝试访问集合中的无效元素则会异常。
断言通常用于测试,AFAIK 不像其他机制那样用作一般错误处理机制。
【讨论】:
断言可用于强制执行不变量。例如,NSParameterAssert(someParam != nil);
将强制执行指定参数不能为 nil 的不变量。
@Kevin Ballard:断言通常是在发布版本中定义的,或者至少是我最后一次检查的,所以就像我说的那样,它们不是一般的错误处理机制。
可以,但默认情况下不会省略。您必须更改构建设置才能获得该行为。以上是关于Objective-C:断言 vs. 异常 vs. 错误的主要内容,如果未能解决你的问题,请参考以下文章
cvMatchTemple 断言失败错误(VS2015+opencv2)