你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> 防御式編程:理解斷言

防御式編程:理解斷言

編輯:IOS開發基礎

225849-2f273b05b28aa088.png

本文授權轉載,作者:沒故事的卓同學(簡書)

在防御式駕駛中要建立這樣一種思維,那就是你永遠也不能確定另一位司機將要做什麼。這樣才能確保在其他人做出危險動作時你也不會受到危害。你要承擔起保護自己的責任,哪怕是其他司機犯的錯誤。防御式編程的主要思想是:子程序應該不因傳入錯誤數據而被破壞,哪怕是由其他子程序產生的錯誤數據。

斷言

斷言是指在開發期間使用的、讓程序在運行時進行自檢的代碼。斷言為真,則表明程序運行正常;斷言為假,則意味著它已經在代碼中發現意料之外的錯誤。

斷言可以用於在代碼中說明各種假定,澄清各種不希望的情形。

斷言主要是用於開發和維護階段。通常,斷言只是在開發階段被編譯到目標代碼中。在開發階段,斷言可以查清相互矛盾的假定、預料之外的情況以及傳給子程序的錯誤數據等。

iOS中的斷言語法

在OC中,斷言使用NSAssert函數,是一個宏。基本形式是兩個參數,第一個參數是一個bool值:斷言的結果,第二個參數是一個描述字符串。斷言的結果為false時,第二個參數描述字符串會輸出。

225849-c74332527f38af28.png

這裡舉例一段BlocksKit中的代碼:

- (instancetype)initWithBlock:(id)block
{
NSParameterAssert(block);
NSMethodSignature *blockSignature = [[self class] typeSignatureForBlock:block];
NSMethodSignature *methodSignature = [[self class] methodSignatureForBlockSignature:blockSignature];
NSAssert(methodSignature, @"Incompatible block: %@", block);
return (self = [self initWithBlock:block methodSignature:methodSignature blockSignature:blockSignature]);
}

斷言通常用來判斷外部輸入的參數是否正確,所以cocoa封裝了一個檢查參數的斷言,與NSAssert相比方便的地方就是自動定義了一個輸出字符串。

NSParameterAssert是一個封裝了NSAssert的宏,定義如下:

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

在來看上面代碼中這句斷言的使用:

NSAssert(methodSignature, @"Incompatible block: %@", block);

這段代碼的意圖是用傳入的block來代替某個對應delegate的方法,所以要檢查一下傳入的block和這個delegate上對應的方法簽名是否一致。如果不一致則中斷程序,拋出“Incompatible block”提示信息。

在swift中,斷言用assert函數。兩個參數和OC中的相同。

public func assert(@autoclosure condition: () -> Bool, @autoclosure _ message: () -> String = default, file: StaticString = #file, line: UInt = #line)

通過聲明可以發現,斷言中會記錄當前文件和行號,但是這裡賦了默認值,所以使用assert時可以省略。

這裡順帶提下代碼規范,有些人有時為了方便會把幾句代碼寫在一行。這樣寫一個潛在的壞處就是如果發生異常,日志中記錄了行號。但是因為這一行有幾句代碼,增加了判斷是由具體哪一句代碼產生異常。應該避免將幾句代碼寫在一行裡。

斷言還有一個經常使用的地方是單元測試。在單元測試中,XCode中封裝了幾個在測試中經常使用的斷言。

這裡列舉AFN單元測試中用到了XCTAssert的代碼,只截取部分帶斷言代碼:

//常規的斷言
XCTAssert([NSStringFromClass([task class]) isEqualToString:@"__NSCFBackgroundDownloadTask"]);
XCTAssertTrue([[[serializedRequest URL] query] isEqualToString:@"key=value"], @"Query parameters have not been serialized correctly (%@)", [[serializedRequest URL] query]);
XCTAssertFalse([policy evaluateServerTrust:trust forDomain:nil], @"Policy should not allow server trust because invalid certificaftes are not allowed");
//為nil時是正確的
XCTAssertNil(error, @"Error handling status code %@", @(statusCode));
//不為nil時是正確的
XCTAssertNotNil(error, @"Did not fail handling status code %@",@(statusCode));
//前兩個表達式結果相等時正確
XCTAssertEqual(reachable, weakManager.isReachable, @"Expected status to match 'isReachable'");

XCT(Xcode Test)中還有很多類似的斷言,有興趣可以自己查文檔,這裡就不列舉了。

斷言使用的原則

用錯誤處理代碼來處理預期會發生的狀況,用斷言來處理絕不應該發生的狀況

錯誤處理代碼用來檢查不太可能經常發生的非正常情況,這些情況在寫代碼時就預料到的,而且在產品代碼中也要處理這些情況。而斷言是用於檢查代碼中的bug,如果在發生異常的時候觸發了斷言,采取的措施就不僅僅是對錯誤做出恰當的反應,而是應該修改源碼並重新編譯。可以把斷言理解成一種注釋,它說明了這個程序的假定。

避免把執行的代碼放到斷言中

斷言的可以在編譯器中設置關閉,如果你把一些操作寫在斷言裡,在某些情況下可能編譯器會過濾掉這些代碼。

NSAssert([self performAction], @"could't perform);

這樣寫就很危險,應該這樣寫:

Bool performed=[self performAction];

NSAssert(performed, @"could't perform);

高健壯性的代碼,先使用斷言再處理錯誤

對於要求高健壯性的代碼,可能項目非常龐大,超長的開發周期和很多的開發人員,也可能出現斷言被觸發但是沒有被注意到,這時應該也處理一下觸發斷言時的錯誤。

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