斷言(NSAssert)在IOS中的應用
NSAssert()是一個巨集,用於開發階段除錯程式中的Bug,通過為NSAssert()傳遞條件表示式來斷定是否屬於Bug,滿足條件返回真值,程式繼續執行,如果返回假值,則丟擲異常,並且可以自定義異常描述。
檢視原始碼我們可以看到NSAssert是這樣定義的,NSAssert(condition,description),condition取相反值.比如判斷一個值不能為空時,NSAssert(param!=nil,@"param 不能為空");
#define NSAssert(condition, desc, ...) \ do { \ __PRAGMA_PUSH_NO_EXTRA_ARG_WARNINGS \ if (__builtin_expect(!(condition), 0)) { \ 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) #endif #if !defined(_NSCAssertBody) #define NSCAssert(condition, desc, ...) \ do { \ __PRAGMA_PUSH_NO_EXTRA_ARG_WARNINGS \ if (__builtin_expect(!(condition), 0)) { \ NSString *__assert_fn__ = [NSString stringWithUTF8String:__PRETTY_FUNCTION__]; \ __assert_fn__ = __assert_fn__ ? __assert_fn__ : @"<Unknown Function>"; \ NSString *__assert_file__ = [NSString stringWithUTF8String:__FILE__]; \ __assert_file__ = __assert_file__ ? __assert_file__ : @"<Unknown File>"; \ [[NSAssertionHandler currentHandler] handleFailureInFunction:__assert_fn__ \ file:__assert_file__ \ lineNumber:__LINE__ description:(desc), ##__VA_ARGS__]; \ } \ __PRAGMA_POP_NO_EXTRA_ARG_WARNINGS \ } while(0) #endif
NSAssert和assert 區別
NSAssert和assert都是斷言,主要的差別是assert在斷言失敗的時候只是簡單的終止程式,而NSAssert會報告出錯誤資訊並且打印出來.所以只使用NSAssert就好,可以不去使用assert。
NSAssert/NSCAssert
iOS中用的最多的是兩對斷言, NSAssert/NSCAssert 和 NSParameterAssert/NSCparameterAssert. 要知道他們的區別,我們先來看看他們定義(如上圖).
從定義可以看出來,前者是適合於ObjectC的方法,_cmd 和 self 與執行時有關. 後者是適用於C的函式。
NSParameterAssert/NSCparameterAssert 兩者的區別也是前者適用於Objective-C的方法,後者適用於C的函式。
實際開發中就用前者就可以了。
NSAssert/NSCAssert 和 NSParameterAssert/NSCparameterAssert 的區別是前者是針對條件斷言, 後者只是針對引數是否存在的斷言, 除錯時候可以結合使用,先判斷引數,再進一步斷言,確認原因.
NSAssert的用法
int a = 1;
NSCAssert(a == 2, @"a must equal to 2"); //第一個引數是條件,如果第一個引數不滿足條件,就會記錄並列印後面的字串
執行則會崩潰並在控制檯輸出資訊如下:
*** Assertion failure in *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'a must equal to 2'
NSParameterAssert的用法
- (void)assertWithPara:(NSString *)str
{
NSParameterAssert(str); //只需要一個引數,如果引數存在程式繼續執行,如果引數為空,則程式停止列印日誌
//further code ...
}
如果 呼叫方法 assertWithPara: 傳入引數為空則有如下日誌
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid parameter not satisfying: str'
日誌中的數字是告訴你 第多少行程式碼出錯了。
Xcode 已經預設將release環境下的斷言取消了, 免除了忘記關閉斷言造成的程式不穩定. 所以不用擔心在開發時候大膽使用。
自定義NSAssertionHandler
NSAssertionHandler例項是自動建立的,用於處理錯誤斷言。如果 NSAssert和NSCAssert條件評估為錯誤,會向 NSAssertionHandler例項傳送一個表示錯誤的字串。每個執行緒都有它自己的NSAssertionHandler例項。
我們可以自定義處理方法,從而使用斷言的時候,控制檯輸出錯誤,但是程式不會直接崩潰。
#import "MyAssertHandler.h"
@implementation MyAssertHandler
//處理Objective-C的斷言
-(void)handleFailureInMethod:(SEL)selector object:(id)object file:(NSString *)fileName lineNumber:(NSInteger)line description:(NSString *)format, ...
{
NSLog(@"NSAssert Failure: Method %@ for object %@ in %@#%li", NSStringFromSelector(selector), object, fileName, (long)line);
}
//處理C語言的斷言
-(void)handleFailureInFunction:(NSString *)functionName file:(NSString *)fileName lineNumber:(NSInteger)line description:(NSString *)format, ...
{
NSLog(@"NSCAssert Failure: Function (%@) in %@#%li", functionName, fileName, (long)line);
}
@end
給執行緒新增處理類
NSAssertionHandler *myHandler = [[MyAssertHandler alloc] init];
//給當前的執行緒處理類
[[[NSThread currentThread] threadDictionary] setValue:myHandler forKey:NSAssertionHandlerKey];