你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> Foundation中的斷言處理

Foundation中的斷言處理

編輯:IOS開發基礎

1.jpg

經常在看一些第三方庫的代碼時,或者自己在寫一些基礎類時,都會用到斷言。所以在此總結一下Objective-C中關於斷言的一些問題。

Foundation中定義了兩組斷言相關的宏,分別是:

NSAssert / NSCAssert
NSParameterAssert / NSCParameterAssert

這兩組宏主要在功能和語義上有所差別,這些區別主要有以下兩點:

  1. 如果我們需要確保方法或函數的輸入參數的正確性,則應該在方法(函數)的頂部使用NSParameterAssert / NSCParameterAssert;而在其它情況下,使用NSAssert / NSCAssert。

  2. 另一個不同是介於C和Objective-C之間。NSAssert / NSParameterAssert應該用於Objective-C的上下文(方法)中,而NSCAssert / NSCParameterAssert應該用於C的上下文(函數)中。

當斷言失敗時,通常是會拋出一個如下所示的異常:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'true is not equal to false'

Foundation為了處理斷言,專門定義了一個NSAssertionHandler來處理斷言的失敗情況。NSAssertionHandler對象是自動創建的,用於處理失敗的斷言。當斷言失敗時,會傳遞一個字符串給NSAssertionHandler對象來描述失敗的原因。每個線程都有自己的NSAssertionHandler對象。當調用時,一個斷言處理器會打印包含方法和類(或函數)的錯誤消息,並引發一個NSInternalInconsistencyException異常。就像上面所看到的一樣。

我們很少直接去調用NSAssertionHandler的斷言處理方法,通常都是自動調用的。

NSAssertionHandler提供的方法並不多,就三個,如下所示:

// 返回與當前線程的NSAssertionHandler對象。
// 如果當前線程沒有相關的斷言處理器,則該方法會創建一個並指定給當前線程
+ (NSAssertionHandler *)currentHandler

// 當NSCAssert或NSCParameterAssert斷言失敗時,會調用這個方法
- (void)handleFailureInFunction:(NSString *)functionName file:(NSString *)object lineNumber:(NSInteger)fileName description:(NSString *)line, format,...

// 當NSAssert或NSParameterAssert斷言失敗時,會調用這個方法
- (void)handleFailureInMethod:(SEL)selector object:(id)object file:(NSString *)fileName lineNumber:(NSInteger)line description:(NSString *)format, ...

另外,還定義了一個常量字符串,

NSString * const NSAssertionHandlerKey;

主要是用於在線程的threadDictionary字典中獲取或設置斷言處理器。

關於斷言,還需要注意的一點是在Xcode 4.2以後,在release版本中斷言是默認關閉的,這是由宏NS_BLOCK_ASSERTIONS來處理的。也就是說,當編譯release版本時,所有的斷言調用都是無效的。

我們可以自定義一個繼承自NSAssertionHandler的斷言處理類,來實現一些我們自己的需求。如Mattt Thompson的NSAssertionHandler實例一樣:

@interface LoggingAssertionHandler : NSAssertionHandler
@end

@implementation LoggingAssertionHandler

- (void)handleFailureInMethod:(SEL)selector
                       object:(id)object
                         file:(NSString *)fileName
                   lineNumber:(NSInteger)line
                  description:(NSString *)format, ...
{
    NSLog(@"NSAssert Failure: Method %@ for object %@ in %@#%i", NSStringFromSelector(selector), object, fileName, line);
}

- (void)handleFailureInFunction:(NSString *)functionName
                           file:(NSString *)fileName
                     lineNumber:(NSInteger)line
                    description:(NSString *)format, ...
{
    NSLog(@"NSCAssert Failure: Function (%@) in %@#%i", functionName, fileName, line);
}

@end

上面說過,每個線程都有自己的斷言處理器。我們可以通過為線程的threadDictionary字典中的NSAssertionHandlerKey指定一個新值,來改變線程的斷言處理器。

如下代碼所示:

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  NSAssertionHandler *assertionHandler = [[LoggingAssertionHandler alloc] init];
  [[[NSThread currentThread] threadDictionary] setValue:assertionHandler
                                                 forKey:NSAssertionHandlerKey];
  // ...

  return YES;
}

而什麼時候應該使用斷言呢?通常我們期望程序按照我們的預期去運行時,如調用的參數為空時流程就無法繼續下去時,可以使用斷言。但另一方面,我們也需要考慮,在這加斷言確實是需要的麼?我們是否可以通過更多的容錯處理來使程序正常運行呢?

Mattt Thompson在NSAssertionHandler中的倒數第二段說得挺有意思,在此摘抄一下:

But if we look deeper into NSAssertionHandler—and indeed, into our own hearts, there are lessons to be learned about our capacity for kindness and compassion; about our ability to forgive others, and to recover from our own missteps. We can't be right all of the time. We all make mistakes. By accepting limitations in ourselves and others, only then are we able to grow as individuals.

參考

  1. NSAssertionHandler

  2. NSAssertionHandler Class Reference

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved