Objective-C錯誤模型

原文鏈接:http://yangchao0033.github.io/blog/2016/01/04/objective-ccuo-wu-mo-xing/

為什么不使用異常處理機制?

事實上,OC是具備異常處理機制的,但是具體情況下不會啟用該機制。原因是ARC下默認不是“異常安全”的。
理由是,如果拋出異常,那么本應該在作用域末尾釋放的對象在現(xiàn)在不能自動釋放了。 如果想生成“異常安全”的代碼,需要設置編譯標志“-fobjc-arc-exceptions”,并且需要引入額外的代碼,在不拋出異常時,也照樣執(zhí)行這段代碼。
及時不適用ARC,也很難在拋出異常后不會導致內存泄露。例如:

    id resource = [YCResource alloc] init] retain];
    if (/*有異常發(fā)生*/) { 
    @throw [NSException exceptionWithName:@"/*異常名稱*/" reason@"/*異常原因*/" userInfo:nil];
    }
    [resource callSomeMethod];
    [resource release]; 

以上代碼:如果一旦有異常拋出,那么之后的代碼都不會被執(zhí)行,資源resource對象也就無法被釋放。
雖然可以在異常發(fā)生前釋放資源對象,但是如果需要釋放的資源對象非常多,之后又有新的資源加入,往往會忘了在異常前釋放新加入的資源。
OC現(xiàn)在所采取的方法是只有在極其罕見的異常下,才拋出異常,異常拋出后不必考慮恢復問題,此時程序應該直接退出,這樣就不用在編寫復雜的“異常安全”代碼了。
現(xiàn)在異常只用于及其嚴重的錯誤(致命錯誤)。
其中一個用途:OC中沒有抽象類,所以無法定義抽象方法,此時,如果你想定義一套抽象 的API,那么可以在方法的實現(xiàn)中拋出異常,告訴使用者需要覆寫該方法:

- (void)absructMethd {
    NSString exceptionReason = [NSString stringWithFormat:@"%@ must be overridden", NSStringFromSelector(_cmd)];
    @throw [NSException exceptionWithName:NSInternalInnconsistencyException reson:exceptionReason userInfo:nil];
}

如何處理“不那么嚴重(非致命)”的錯誤?

OC的范式是:另方法返回0/nil,或者使用NSError。

如何使用NSError

NSErrow使用起來非常靈活,我們使用它告訴調用者發(fā)生了什么錯誤。
NSError構成:

  • Error domain(錯誤范圍)
    描述錯誤發(fā)生的范圍。例如處理URL的子系統(tǒng),當url解析出現(xiàn)錯誤,就可以使用NSURLErrorDomain來表示錯誤范圍。
  • Error code(錯誤碼)
    獨有的錯誤代碼,用以表明具體發(fā)生了何種錯誤,一般用枚舉定義,HTTP請求中可以存儲狀態(tài)碼。
  • User info(用戶信息)
    有關錯誤的額外附加信息,可以是一段本地化描述,也可能是用來存儲該錯誤是由其他錯誤引起的,最終生成一條完整的錯誤鏈。

其應用場景類型:

  • 在代理協(xié)議中返回NSError錯誤。
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError - *)error;

這樣,接口定義者和使用者都可以選擇是否輸入并處理該錯誤。

  • 在方法中用對象指針來監(jiān)測錯誤。
- (BOOL)doSomething:(NSError **)error;
/*使用方法*/
NSError *error = nil;
BOOL ret = [slef doSomething:&error];
if (ret) {
        // 處理錯誤
}

這種方法一般都會返回BOOL值,用來判斷是否操作成功,如果只是判斷成功狀態(tài),則只要判斷返回值并且error參數(shù)傳為nil就行,而error指針可以用來輸出錯誤信息。
筆記:實際上ARC時,編譯器會把NSError**轉換為NSError*__autorelease*,當doSomething方法執(zhí)行完畢后,會將調用者創(chuàng)建的對象自動釋放掉。這是為了防止調用者不一定能確保會釋放掉NSError對象,必須要求他自己可以自動釋放掉,所以加入autorelease。這樣就與打部分返回值具備的語義相同了。

doSomething方法的實現(xiàn)原理:

- (BOOL)doSomething:(NSError *)error {
        if(/*發(fā)生error*/) {
                if(error) {
                /*給外部傳入?yún)?shù)初始化*/
                *error = [NSError errorWithDomain:domain code:code userinfo:userInfo];
                }
                return NO;
        } else {
                return YES;
        }
}

在給error解引用--*error之前,必須保證error不為空。

一般情況下,給error對象填入恰當?shù)腻e誤范圍,錯誤碼,錯誤信息后,調用者就可以更精準的定位錯誤。
我們一般對錯誤范圍使用NSString全局變量進行定義。錯誤碼常采用枚舉,例如:

extern NSSring *const YCErrorDomain;
typedef NS_ENUM(NSInteger, YCError){
    YCErrorUnknow   = -1,
    YCErrorInternalIncosistency  = 100,
    YCErrorGeneralFault  = 105,
    YCErrorBadInput  = 500,
};
NSString *const YCErrorDomain = @"YCErrorDomain";
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容