断言和先决条件发是运行时发生的检测。使用它们可以确保在执行后面的代码之前已经满足了条件。如果断言或者先决条件中的值是true,代码像原来一样继续执行。如果条件是false,程序当前的状态就是无效的,代码会结束执行,app也会停止运行。
使用断言和先决条件来表现出你编写代码的时候做的假设和希望,并让他们成为你代码的一部分。断言帮助你在开发过程中发现错误和不合适的假设。先决条件帮助你在生产环境下检测问题。
除了在运行时验证你的期望,断言和先决条件也可以成为代码中的文档,这很有用。和上一章讨论的错误处理不一样,断言和先决条件不是用来处理可恢复的或者预期会发生的错误的。因为当一个断言或者先决条件失败了,那么程序当前的状态就是无效的,没有办法捕获一个发生了错误的断言。
使用断言或者先决条件,不是为了把代码设计成不会出现不符合条件的样子。使用它们加强数据和状态的有效性使你可以预见到app在出现不可用状态的时候会导致结束,也使问题更加容易被调试。只要侦测到异常就结束执行也可以最小化无效的状态带来的危害。
断言和先决条件的不同之处在于它们检测的时机不一样:断言只在调试模式中检测,先决条件在调试模式和产品模式都会检测。在产品模式下,断言中的条件不会被检测,也就是说在开发过程中,无论使用多少断言,都不会影响最后的产品的性能。
使用断言调试(Debugging with Assertions)
我们通过调用Swift标准库的assert(_:_:file:line:)方法来书写断言。传入一个结果是true或者false的表达式和一个当条件是false的时候要展示的信息,比如:
let age = -3
assert(age >= 0, "A person's age can't be less than zero.")
// 这个断言会失败, 应该 -3 >= 0 结果是false.
这个例子中,如果 age>=0是true,也就是age如果是一个非负数,代码会继续执行。如果age的值是一个负数,在上面的代码中,age>=0就是false,然后断言会失败,应用会结束。
你也可以省略断言的信息,比如,只是需要重复检查条件:
assert(age >= 0)
如果代码中已经检查了条件,你可以使用assertionFailure(_:file:line:)函数表明这个断言失败了,比如:
if age > 10 {
print("You can ride the roller-coaster or the ferris wheel.")
} else if age > 0 {
print("You can ride the ferris wheel.")
} else {
assertionFailure("A person's age can't be less than zero.")
}
强制执行先决条件(Enforcing Preconditions)
当条件有可能是false的情况下,使用先决条件,但是需要确定只有当条件是true的时候你的代码才会继续执行。例如,使用先决条件判断一个下标是不是越界了,或者检查入参是不是有效。
可以通过调用precondition(_:_:file:line:)函数书写先决条件。传入一个结果是true或者false的表达式和一个当条件是false的时候要展示的信息,比如:
// In the implementation of a subscript...
//引用于subscript的实现
precondition(index > 0, "Index must be greater than zero.")
你可以调用preconditionFailure(_:file:line:)函数来表明先决条件失败了,例如,在一个swift语句的defaultcase里面,所有有效的数据应该在switch其他的case里面已经被处理了,那么在defaultcase里面就可以使先决条件失败。
NOET:如果编译器在不检查的模式(-Ounchecked),先决条件不会被检查。编译器会假设所有的先决条件都是成功的。因此,编译器会优化你的代码。然而fatalError(_:file:line:)函数总是会停止执行代码,不理会是否有优化的设置。在编写Demo或者开发的早期,可以为一些尚未完成的功能创造一些占位方法,并用fatalError("Unimplemented")作为占位方法的实现。因为和断言或者先决条件不一样,致命的错误是不会被优化的,所以你可以确定当碰到一些没有实现的占位方法,程序总会因为碰到致命的错误而停止运行。