【问题标题】:Fail safe assertions in SwiftSwift 中的故障安全断言
【发布时间】:2015-09-17 20:54:58
【问题描述】:

我通常在 Objective-C 中使用断言来断言一个值。在调试版本中,我断言是为了停止程序的执行并检查我的假设是否不正确。但是,在生产构建中,我找到了一种安全失败的方法,以尽量减少对用户的影响。我通过创建一个宏来实现这一点,该宏将 NSAssert 封装在 if 语句中,该语句还执行我想在生产中作为故障安全运行的代码。例如:

我将使用的断言宏:

#define AssertTrueOrExecute(condition, action) \
  if (!condition) { \
    NSAssert(testCondition, @"Condition failed"); \
    action; \
  }

在我的应用程序的某个地方,我会有这样的东西:

- (void)someMethod
{
  BOOL testCondition = ...
  // Ensure the testCondition is true before proceeding any further
  AssertTrueOrExecute(testCondition, return);
  // Potentially unsafe code that never gets executed if testCondition is false
}

- (void)someReturningMethod
{
  BOOL testCondition = ...
  // Ensure the testCondition is true before proceeding any further
  AssertTrueOrExecute(testCondition, return @"safe string");
  // Potentially unsafe code that never gets executed if testCondition is false
}

由于我无法定义像 Swift 中提到的那样的宏,有没有办法具有相同的行为?那我将如何为我的 AssertTrueOrExecute 宏提供一个 Swift 等效项?

更新:

为了进一步解释这个问题,如果我使用的是 Swift,我目前会这样写:

func someMethod () {
    let testCondition : Bool = ...

    // Ensure the testCondition is true before proceeding any further
    if (!testCondition) {
      assert(testCondition);
      return;
    }
    // Potentially unsafe code that never gets executed if testCondition is false
}

所以问题更多的是如何将带有断言的 if 语句以与我有 Objective-C 宏的类似方式包装,以便我可以断言或提前返回?

更新 2:

另一个例子是在函数中返回一些东西,例如:

func someReturningMethod () -> String {
    let testCondition : Bool = ...

    // Ensure the testCondition is true before proceeding any further
    if (!testCondition) {
      assert(testCondition);
      return "safe string";
    }    
    // Potentially unsafe code that never gets executed if testCondition is false
    return "some other string"
}

【问题讨论】:

    标签: objective-c swift macros


    【解决方案1】:

    在 Swift 中没有宏,但在 Swift 中可能有其他方法可以实现与在 Objective-C 中相同的功能。

    然而,这里真正的问题是,你试图以一种你真的不应该的方式解决问题:

    不要混用程序员错误运行时错误

    相反,明确区分程序员错误运行时错误。用断言处理程序员错误,用NSError 处理运行时错误,在Swift 中分别用try & catchthrow

    请注意,程序员错误的“范围”并不局限于程序因断言失败而失败的点:这种错误很可能会产生不良的副作用,使程序处于无效状态,而且通常这断言检测可能在断言失败之前很长时间已经发生的错误。因此,当断言失败时,您的程序很可能已经处于无效状态。

    一个经验法则是,断言失败不应该发生在生产代码中(阅读 MUST NOT)。嗯,这些是程序员错误,应该被修复,不是吗?您在单元测试中使用断言来验证您的假设。如果您仍然担心您的假设可能会在生产中中断,并且还确定这不是运行时错误(应该始终优雅地处理),它应该停止程序 - 无论如何,所有的赌注都是关闭的。在 Swift 中,您可以为此使用 fatalError

    有时,违反某个假设是程序员错误还是运行时错误并不总是那么明显,可能取决于上下文。但是,作为程序员,您始终可以定义它是什么。以字符串参数为例:如果您直接从想要创建帐户的用户输入的文本字段中获取它并被要求输入他的姓名,您应该验证字符串并在它不适合时返回/抛出错误您的期望 - 例如,如果它是空的、太短等。也就是说,在 Swift 中,您可能会 throw 一个错误并在调用站点上优雅地处理它,可能在视图控制器中。另一方面,您定义 初始化一个User 对象的name 将为空是没有意义的。也就是说,在您的 init 例程中定义有效用户名不能为空的前提条件,并使用assertfatalError 进行检查。在这种情况下,您的程序是正确的,当没有代码路径初始化名称为空的用户时。

    【讨论】:

    • 感谢您的回答!尽管这可能不是我一直在寻找的答案,但它确实提供了很多关于错误处理方法的见解。我使用断言来捕获程序员错误,但考虑到 Swift 中的类型安全性,无论如何我可能捕获的错误更少。在这一点上,我对 Swift 的体验非常有限,所以即使我可能不同意这样一个事实,即有时可能会因程序员错误而优雅地失败而不必让应用程序崩溃,但我很难在 Swift 中找到一个很好的例子。
    • @lucianomarisi 你可能很难在 Swift 中找到一个好的例子,因为没有这样的好例子。如果你来自 C#,我们会发现很多 ;) 在 Swift 中,当你确实想优雅地处理任何错误时,你需要使用 Swift 的try/catch & throw 机制,它们是不是异常(如 C++ 或 C# 等),但只是 NSError / ErrorType 的语法糖。
    猜你喜欢
    • 1970-01-01
    • 2012-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-14
    • 1970-01-01
    • 2015-12-17
    • 1970-01-01
    相关资源
    最近更新 更多