实际上,NSAssert 的意义何在?

Posted

技术标签:

【中文标题】实际上,NSAssert 的意义何在?【英文标题】:What's the point of NSAssert, actually? 【发布时间】:2010-11-25 10:28:36 【问题描述】:

我不得不问这个问题,因为:我唯一认识到的是,如果断言失败,应用程序就会崩溃。这就是使用 NSAssert 的原因吗?或者它还有什么好处?将 NSAssert 放在我在代码中所做的任何假设之上是否正确,例如一个永远不应该接收 -1 作为参数但可能是 -0.9 或 -1.1 的函数?

【问题讨论】:

【参考方案1】:

断言是为了确保一个值是它应该是的。如果断言失败,则意味着出现问题,因此应用程序退出。使用 assert 的一个原因是,如果您有一些函数不会运行或会产生非常糟糕的副作用,如果传递给它的参数之一不完全是某个值(或值范围),您可以放置​​一个断言确保该值是您期望的值,如果不是,则确实有问题,因此应用程序退出。 Assert 对于调试/单元测试非常有用,当你提供框架来阻止用户做“邪恶”的事情时也是如此。

【讨论】:

您应该取出 NSAssert 进行释放。有一个编译时标志可以做到这一点。 > 你应该拿出 NSAssert 来释放。这是值得商榷的。我总是在启用断言的情况下发布我的应用程序,这是许多软件的标准做法,例如 Apple 就是这样做的。一旦您的程序检测到异常状态,您就应该崩溃。您可以获得错误发生位置的堆栈跟踪,而如果您禁用断言,您最终可能会损坏内存和/或用户数据,并且问题将很难调试。 请注意,XCode 4 在发布配置中默认定义了 NS_BLOCK_ASSERTIONS。我想如果你不改变你发布的代码不会包含 NSAssert:s. 如果我理解正确,离开它们有什么意义(在发行版上)?为什么不用 if 语句替换 NSAssert,如果(发生了可怕的事情),然后通知用户(或做一些在 your 控制下的事情),而不仅仅是退出/崩溃并离开用户想知道发生了什么……还是我错过了什么? 在正常情况下根本不应该发生的每一个例外情况下,都是浪费开发人员的时间。这涉及考虑适当的方式来通知用户,和/或使应用程序足够健壮,以便在它们发生后以预期的方式继续运行。更实用的方法是使应用程序崩溃并修复从崩溃报告中发现的错误并发布新版本。话虽如此,重要的是要确保在任何此类情况下都不会丢失数据。尽管如此,这必须得到保证,但工作量要小得多。【参考方案2】:

断言通常用于强制执行特定方法或逻辑片段的预期用途。假设您正在编写一个计算两个大于零整数之和的方法。为了确保该方法始终按预期使用,您可能会放置一个断言来测试该条件。

简短回答:他们强制您的代码仅按预期使用。

【讨论】:

【参考方案3】:

NSAssert(及其 stdlib 等效项assert)用于在开发过程中检测编程错误。在生产(已发布)应用程序中,您永远不应该有一个失败的断言。因此,您可能会断言您永远不会将负数传递给需要正参数的方法。如果断言在测试期间失败了,那么你就有了一个错误。但是,如果传递的值是由用户输入的,则您需要对输入进行适当的验证,而不是依赖于生产中的断言(您可以为禁用 NSAssert* 的发布版本设置 #define。

【讨论】:

+1 因为您的回答对我来说最有意义!如果 NSAssert 用于开发用途,而不是在发布后使​​用,则使用它更有意义。用户输入不允许的值后应出现 UI 错误,而不是 NSAssert 使应用程序崩溃。雾散了!【参考方案4】:

我无法真正与 NSAssert 交谈,但我想它的工作原理类似于 C 的 assert()。

assert() 用于在代码中强制执行语义契约。你问这是什么意思?

嗯,就像你说的:如果你有一个永远不应该收到 -1 的函数,你可以让 assert() 强制执行:

无效 gimme_positive_ints(int i) 断言(我> 0);

现在您会在错误日志(或 STDERR)中看到类似的内容:

断言 i > 0 失败:文件 example.c,第 2 行

因此,它不仅可以防止潜在的错误输入,而且可以以有用的标准方式记录它们。

哦,至少在 C 中 assert() 是一个宏,因此您可以在发布代码中将 assert() 重新定义为无操作。我不知道 NSAssert 是否是这种情况(甚至是 assert()),但编译出这些检查非常有用。

【讨论】:

是的,NSAssert 也是一个宏。【参考方案5】:

除了上面大家说的,NSAssert() 的默认行为(不像 C 的assert())是抛出异常,你可以捕获和处理。例如,Xcode 就是这样做的。

【讨论】:

是否有更多关于我们如何捕获和处理异常的信息? cocoa 中的异常实际上不是“可捕获和可处理的”。如果控制在调用树的任何地方都通过苹果函数,则行为是未定义的。异常纯粹是为了错误报告(又名,crittercism 等),而不是像在 Java 中那样用于一般用途。【参考方案6】:

要完全回答他的问题,任何类型的断言都是为了帮助调试。更有价值的是从源头捕获错误,然后在它们导致崩溃时在调试器中捕获它们。

例如,您可以将一个值传递给一个函数,该函数期望某个范围内的值。该函数可以存储该值以供以后使用,并在以后使用时应用程序崩溃。在这种情况下看到的调用堆栈不会显示错误值的来源。最好在出现错误值时捕获错误值,以找出谁传递了错误值以及原因。

【讨论】:

【参考方案7】:

只是为了澄清,正如有人提到但没有完全解释的那样,拥有和使用断言而不是仅仅创建自定义代码(例如,执行 if 并为不良数据引发异常)的原因是断言应该在生产中被禁用应用程序。

在开发和调试时,您可以使用断言来捕获错误。当断言被评估为假时,程序将停止。 但是,在为生产进行编译时,编译器会省略断言代码,实际上让您的程序运行得更快。到那时,希望你已经修复了所有的错误。 如果您的程序在生产中仍然存在错误(当断言被禁用并且程序“跳过”断言时),您的程序可能最终会在其他时候崩溃。

来自 NSAssert 的帮助:“如果定义了预处理器宏 NS_BLOCK_ASSERTIONS,则断言将被禁用。” 因此,只需将宏放入您的分发目标中[仅]。

【讨论】:

【参考方案8】:

值得指出的是,除了运行时检查之外,断言编程是您根据合同设计代码时使用的重要工具。

关于断言和合同设计主题的更多信息可以在下面找到:

Assertion (software development)

Design by contract

Programming With Assertions

Design by Contract, by Example [Paperback]

【讨论】:

【参考方案9】:

NSAssert 不仅仅让应用崩溃。它告诉您断言发生的类、方法和行。所有断言也可以使用 NS_BLOCK_ASSERTIONS 轻松停用。从而使其更适合调试。另一方面,抛出 NSException 只会使应用程序崩溃。它也没有说明异常的位置,也不能这么简单地禁用它。请参阅下图中的差异。

应用程序崩溃是因为断言也会引发异常,正如NSAssert documentation 所述:

调用时,断言处理程序会打印一条错误消息, 包括方法和类名(或函数名)。那么它 引发 NSInternalInconsistencyException 异常。

NSAssert:

NSException:

【讨论】:

NSException 提供了大量机会来自定义它通过reasonuserInfo 参数返回的输出。您没有理由不能添加类名、选择器、行信息以及您想添加的任何其他内容以帮助调试。恕我直言,您在开发过程中使用NSAssert 进行调试,但禁用它们发货;如果你想在运输代码中留下一个断言,你抛出一个NSException【参考方案10】:

NSAssert 匹配条件时应用崩溃。如果与条件不匹配,则将执行下一条语句。寻找下面的 EX:

我只是创建一个应用来测试NSAssert的任务是什么:

    - (void)viewDidLoad 
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [self testingFunction:2];


-(void)testingFunction: (int)anNum
    // if anNum < 2 -> the app will crash
    // and the NSLog statement will not execute
    // that mean you cannot see the string: "This statement will execute when anNum < 2"
    // into the log console window of Xcode
    NSAssert(anNum >= 2, @"number you enter less than 2");
    // If anNum >= 2 -> the app will not crash and the below 
    // statement will execute
    NSLog(@"This statement will execute when anNum < 2");

进入我的代码应用程序不会崩溃。测试用例是:

anNum >= 2 -> 应用程序不会崩溃,您可以在输出日志控制台窗口中看到日志字符串:“This statement will execute when anNum anNum 应用会崩溃,看不到日志字符串:"This statement will execute when anNum

【讨论】:

反之亦然。 “NSAssert 与条件匹配时使应用程序崩溃。如果与条件不匹配,将执行下一个语句”。 NSAssert 如果不符合条件则使应用程序崩溃,如果符合条件则正常执行。 应用程序在不满足条件时崩溃并记录消息,否则它会进一步执行。

以上是关于实际上,NSAssert 的意义何在?的主要内容,如果未能解决你的问题,请参考以下文章

noreturn 的意义何在?

严格声明的意义何在?

-primitiveValueForKey: 的意义何在?

ISerializable 接口的意义何在?

Kotlin 中使用函数的意义何在

Windows 上 Mono 的意义何在