Osheep

时光不回头,当下最重要。

NSAssert和NSAssertion Handler

NSAssert

NSAssert 是Foundation库中定义的一个宏,我们用它来在Objective-C代码中做断言处理,在开发阶段调试程序中的bug。

我们先来看看这个宏的声明:

#define NSAssert(condition, desc, ...)

参数condition是一个条件表达式,desc通常是一个字符串,被用做异常描述。NSAssert()可以出现在程序中的任何一个位置,当condition表达式为真值(YES)时,程序正常运行,当conditon值为NO时,程序会崩溃,并抛出带有desc的异常信息。

用法示例:

 int a = 0;
NSAssert(a, @"a不能为0");
NSLog(@"do something next");

崩溃信息:

*** Assertion failure in -[ViewController viewDidLoad], ViewController.m:21
 *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'a不能为0'

可以看到程序会在代码21行处'' NSAssert(a, @"a不能为0");崩溃,并抛出相关信息

NSAssertion Handler

我们可以阅读NSAssert宏的定义来看看异常信息是怎么抛出的

define NSAssert(condition, desc, ...)    \
do {                \
__PRAGMA_PUSH_NO_EXTRA_ARG_WARNINGS \
if (!(condition)) {        \
NSString *__assert_file__ = [NSString stringWithUTF8String:__FILE__]; \
__assert_file__ = __assert_file__ ? __assert_file__ : @"<Unknown File>"; \
[[NSAssertionHandler currentHandler] handleFailureInMethod:_cmd \
object:self file:__assert_file__ \
lineNumber:__LINE__ description:(desc), ##__VA_ARGS__]; \
}                \
__PRAGMA_POP_NO_EXTRA_ARG_WARNINGS \
} while(0)

我们可以看到抛出的有关异常信息是NSAssertionHandler类的handleFailureInMethod: object: file: lineNumber: description:方法处理的。

NSAssertionHandler实例是自动创建的,用于处理错误断言。每个线程都可以指定断言处理器,大部分情况下,你只需在-application:didFinishLaunchingWithOptions: 中设置当前线程的断言处理器。

我们可以构建一个NSAssertionHandler的子类并重写相关方法,这样可以避免程序崩溃,并排出自定义格式的异常信息。

//ASAssertionHandle.m
-(void)handleFailureInMethod:(SEL)selector object:(id)object file:(NSString *)fileName lineNumber:(NSInteger)line description:(NSString *)format, ...{

NSLog(@"NSAssert Failure: Method %@ for object %@ in %@ :line:%li \n %@", NSStringFromSelector(selector), object, fileName, (long)line,format);

}

当前线程指定断言处理器:

//AppDelegate.m
(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    ASAssertionHandle *assertionHandle = [[ASAssertionHandle alloc]init];
    [[[NSThread currentThread] threadDictionary] setValue:assertionHandle forKey:NSAssertionHandlerKey];
//

return YES;
}

控制台信息:

 NSAssert Failure: Method viewDidLoad for object <ViewController: 0x7fad31406600> in ViewController.m :line:21 
 a不能为0
 do something next

自定义NSAssertionHandler后,程序不会崩溃,会继续运行并抛出相关信息。

NSParameterAssert

如果你知道了怎么使用NSAssert,那么NSParameterAssert将会很好理解,我们看一下它的宏定义:

define NSParameterAssert(condition) NSAssert((condition), @"Invalid parameter not satisfying: %@", @#condition)

它的用法和断言机制和NSAssert完全一致,我们可以从默认打印信息中了解到,这个宏主要是为方法参数断言时使用。

使用时机

距许多经验丰富的 Objective-C 开发者告诫,我们最好不要在生产环境(release)中使用断言处理器,而只是在开发过程中监控不应该出现的情况。
好在现今版本的Xcode已经帮我们默认取消了release环境下的断言:

《NSAssert和NSAssertion Handler》

参考:NSAssertion​Handler

点赞