Swift中的失败安全断言

时间:2015-09-17 20:54:59

标签: objective-c swift macros

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

我将使用的断言宏:

#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"
}

1 个答案:

答案 0 :(得分:0)

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

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

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

相反,明确区分程序员错误是什么以及运行时错误是什么。处理带有断言的程序员错误,并分别使用NSError&和Swift处理try处理运行时错误。 catchthrow

注意,"范围"程序员错误的限制并不局限于程序因断言失败而失败的情况:很可能这样的错误会产生不良副作用,导致程序处于无效状态,并且通常此断言会检测可能发生的错误断言失败前的时间。因此,当断言失败时,您的程序很可能已经处于无效状态。

根据经验,断言失败不应该发生在生产代码中(不得读)。好吧,这些是程序员错误,应该修复,不应该吗?您可以使用单元测试中的断言来验证您的假设。如果您仍然担心,您的假设可能会在生产中中断并且也确定这不是运行时错误(应该始终优雅地处理),它应该停止程序 - 所有投注都是关闭的。在Swift中,您可以使用fatalError

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