使用断言可以有效地防止程序错误。断言要求程序中特定的语句必须为真。如果不为真,说明程序正处于一种无法预测的运行状态,这时候程序不应该继续执行下去。下面是NSAssert
的一个例子:
如果测试条件返回NO
,NSAssert
就会抛一个异常。异常处理程序捕获异常之后,会调用abort
结束程序。Mac开发中,出现异常时只结束当前循环。而在iOS中,在哪个线程中发生异常,默认行为都是调用abort
结束整个程序。
从技术角度来说,abort
向进程发送一个SIGABRT
信号。信号处理程序会捕获这个信号然后做出相应处理。不建议自己捕获SIGABRT
信号,除非是要用在crash报告中。10.4节“捕获与报告程序崩溃”会详细介绍如何处理程序崩溃。
可以在编译设置面板的“Preprocessor Macros”(GCC_PREPROCESSOR_DEFINITIONS
)中设置NS_BLOCK_ASSERTIONS
以禁用NSAssert
。至于最终发布代码中是否需要禁用NSAssert
,不同的人有不同的答案。这取决于当程序处于非法状态的时候,你是希望它停止运行还是希望它紊乱运行。建议在发布版代码中禁用断言。在某些情况中程序错误可能只会造成非常小的问题,但是断言会导致程序崩溃。Xcode 4在默认情况下会禁用发布版代码中的断言。
虽然我会在发布版代码中删除断言,但是我并不会忽视断言错误。实际上,断言错误属于“永远不该发生”的错误,应该记录到日志中。设置NS_BLOCK_ASSERTIONS
会从程序中完全删除断言,而我建议对断言做些改动以便在日志中留下记录。下面代码中的RNLogBug
(NSLog
的一个别名)函数可以用来记录日志。不建议经常使用#define
,不过用在这里是非常必要的,因为需要把__FILE__
和__LINE__
转换为调用者代码所在的文件和行号。
下面的代码把NSCAssert
包装为RNCAssert
,还定义了一个辅助函数RNAbstract
。在C语言中使用断言的时候应该使用NSAssert
。
RNAssert.h
断言应该位于导致程序崩溃的代码之前。看下面的例子(假设你使用RNAssert
记录日志,包括在发布版代码中):
如果这里会导致断言失败,那么即使关闭断言程序也依然会崩溃。所以要将代码改为下面这样:
这样就好多了,RNAssert
可以记录日志。但是这里有冗余代码,如果断言条件和if
条件不匹配,就可能产生bug。建议使用下面的方式:
这样就保证了断言条件跟if
条件总是匹配,这是一种比较好的断言使用方式。另外,建议在switch
语句的default
分支中使用断言:
这样一来,如果foo
的可能值增加了而switch
块没有进行相应更新,那就会导致断言失败。