錯誤處理 - Dealing with Errors

2018-08-12 21:19 更新

錯誤處理 - Dealing with Errors

幾乎所有的 APP 都會出現(xiàn)錯誤。一些錯誤可能會在你的可控范圍之外,例如硬盤空間耗盡或者網(wǎng)絡(luò)連接中斷。另一些錯誤卻是可恢復(fù)的,例如無效用戶輸入。當(dāng)開發(fā)者在不斷追逐完美的過程中,也可能會伴隨著偶爾的編程錯誤的出現(xiàn)。 如果你來自于其他的語言和開發(fā)平臺,你也許會習(xí)慣于處理大多數(shù)錯誤處理中的異常。當(dāng)你用 ObjC 編程的時候,異常僅會出現(xiàn)在編程錯誤中,就像數(shù)組越界訪問或無效方法參數(shù)。這些就是在你的 APP 上線前的測試中,你需要排查并修復(fù)的問題。 NSError 類的實例代表了所有其他的錯誤。這一章我們將簡單的介紹一下 NSError 對象的使用,包括怎樣處理構(gòu)造方法可能出現(xiàn)的失敗和返回錯誤。更多信息參見?Error Handling Programming Guide

對大多數(shù)的錯誤使用 NSError

錯誤是任何APP 生命周期中不可避免的一部分,假設(shè)你需要向一個遠程網(wǎng)絡(luò)服務(wù)器請求數(shù)據(jù),在這個過程中有多種潛在問題可能出現(xiàn),包括:

  • 無網(wǎng)絡(luò)連接
  • 遠程網(wǎng)絡(luò)服務(wù)器不可訪問
  • 遠程網(wǎng)絡(luò)服務(wù)器不能提供你請求的信息
  • 得到的數(shù)據(jù)與你期望的不匹配
  • 遺憾的是,建立所有可能問題的應(yīng)急計劃與方案是不現(xiàn)實的。相反你必須為可能出現(xiàn)的錯誤籌劃并且知道如何解決,從而獲得最好的用戶體驗。

一些授權(quán)方法( delegate method )向你提醒錯誤

如果你正實現(xiàn)一個授權(quán)對象,它是與一個執(zhí)行某任務(wù)的構(gòu)造類( framework class )一起使用的,比如這個對象需要從遠程服務(wù)器上下載信息。通常,你會發(fā)現(xiàn)你需要至少實現(xiàn)一個錯誤關(guān)聯(lián)方法(error-related method)。例如 包括了一個 connection:didFailWithError:? 方法的NSURLConnectionDelegate 協(xié)議:

    - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;

當(dāng)一個錯誤發(fā)生時,授權(quán)方法將會被調(diào)用,以向你提供一個 NSError 對象來描述這個錯誤。 一個NSError對象包含一個數(shù)字錯誤代碼,域名和描述,以及封裝在一個用戶信息字典里的其他相關(guān)信息。 比起做出讓所有可能錯誤都具有唯一的數(shù)字代碼的要求,Cocoa 和 Cocoa Touch 的錯誤被劃分成域。例如一個錯誤發(fā)生在?NSURLConnection 中,NSURLErrorDomain 域中的? connection:didFailWithError: 方法將會報一個錯。錯誤對象還包含一個本地化的描述,例如“找不到指定主機名的服務(wù)器”。

一些方法可以通過引用傳遞錯誤

一些Cocoa 和 Cocoa Touch 的API 通過引用傳回錯誤,舉個例子,你可能決定通過 ?NSData? 的 ?writeToURL:options:error: 方法,將你從網(wǎng)絡(luò)服務(wù)器獲得的信息通過存入硬盤的形式保存下來。這個方法的最后一個參數(shù)是一個指向 NSError 指針的引用。

    - (BOOL)writeToURL:(NSURL *)aURL
    options:(NSDataWritingOptions)mask
    error:(NSError **)errorPtr;

在你調(diào)用這個方法之前,你需要創(chuàng)建一個合適的指針來傳遞地址:

    NSError *anyError;
    BOOL success = [receivedData writeToURL:someLocalFileURL
                                    options:0
                                    error:&anyError];
    if (!success) {
    NSLog(@"Write failed with error: %@", anyError);
    // present error to user
    }

當(dāng)錯誤發(fā)生時,writeToURL:options:error: 方法會返回 NO ,并會更新你的 anyError指針,指向一個錯誤類來描述出現(xiàn)的問題。在處理應(yīng)用傳遞的錯誤時,重要的是去檢測方法的返回值以確定是否有錯誤發(fā)生。不要只是測試是否有設(shè)置指向錯誤的指針。

提示: 如果你并不關(guān)心出錯的對象,則只需要將 NULL 傳遞給 error: 參數(shù)

如果可以,盡量將錯誤恢復(fù)或顯示給用戶

對你的 APP 來說最好的用戶體驗是隱蔽地從錯誤中恢復(fù)。例如,你正在向遠程網(wǎng)絡(luò)服務(wù)器發(fā)出請求,如果此時發(fā)生了錯誤,你可以試著再向另一個服務(wù)器發(fā)送請求,或者你可以向用戶請求更多的信息,比如有效的用戶名和密碼,然后再次發(fā)出請求。 當(dāng)從錯誤中恢復(fù)是不可能的時候,你才應(yīng)該去警告用戶。如果你正在使用 iOS的Cocoa Touch進行開發(fā),你需要創(chuàng)建一個 UIAlertView 并調(diào)節(jié)它,從而向用戶顯示錯誤。如果你正在使用 OS X 的 Cocoa ,你可以在任何一個 NSResponder 對象(像是一個界面、窗口甚至是應(yīng)用程序?qū)ο蟊旧恚┥险{(diào)用? presentError:? ,然后這個錯誤便會被傳輸?shù)巾憫?yīng)鏈上,以進行進一步的調(diào)試或恢復(fù)。當(dāng)它到達應(yīng)用程序?qū)ο髸r,應(yīng)用程序會通過一個警告板將錯誤呈現(xiàn)給用戶。更多關(guān)于向用戶呈現(xiàn)錯誤,參看?Displaying Information From Error Objects

生成你自己的錯誤信息

為創(chuàng)建你自己的 NSError 類,你需要通過以下格式定義你自己的錯誤類:

    com.companyName.appOrFrameworkName.ErrorDomain

你需要為每一個可能發(fā)生在你的錯誤域中的錯誤選擇一個唯一的錯誤編碼,同時還有合適的錯誤描述,關(guān)于錯誤的描述將會被存儲在用戶信息字典中。像下面這樣:

    NSString *domain = @"com.MyCompany.MyApplication.ErrorDomain";
    NSString *desc = NSLocalizedString(@"Unable to…", @"");
    NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc };

    NSError *error = [NSError errorWithDomain:domain
                                         code:-101
                                     userInfo:userInfo];

這個例子使用了 NSLocalizedDescriptionKey 函數(shù)來查看來自 ?Localizable.strings 文件中,錯誤描述的本地化版本,即在本地化字符串資源中描述的那樣。

如果你需要像早前描述的那樣,通過引用將錯誤傳回,你的方法標(biāo)識中還需要包含一個為指針設(shè)置的參數(shù),用來指向一個 NSError 對象。你同時還需要利用返回的值指明是成功還是失敗,像是這樣:

    - (BOOL)doSomethingThatMayGenerateAnError:(NSError **)errorPtr;

當(dāng)錯誤發(fā)生時,在返回表示失敗的NO之前,你需要首先檢查為錯誤參數(shù)提供的指針值是否為空( NULL ),然后才能取消引用來設(shè)置錯誤信息:

    - (BOOL)doSomethingThatMayGenerateAnError:(NSError **)errorPtr {
    ...
    // error occurred
    if (errorPtr) {
        *errorPtr = [NSError errorWithDomain:...
                                        code:...
                                    userInfo:...];
      }
       return NO;
    }

用于編程錯誤的異常

ObjC 用與其他編程語言類似的方式支持異常,并且與這些語言,如 C++ 、Java ,支持的語法也很相似。就像 NSError 一樣,在 Cocoa 和 Cocoa Touch 里的異常,也是以 NSException 類實例為代表的對象。 如果你編寫的代碼可能導(dǎo)致異常拋出,你可以將這段代碼封裝在一個 try-catch 塊內(nèi):

     @try {
        // do something that might throw an exception
    }
    @catch (NSException *exception) {
        // deal with the exception
    }
    @finally {
        // optional block of clean-up code
        // executed whether or not an exception occurred
    }

如果異常拋出發(fā)生在 @try 塊內(nèi),那么它將會被 @catch 塊捕捉,以方便你對它的處理。比如你在用使用異常作為錯誤處理的低級別C++ 庫進行開發(fā),你可能會捕捉到異常,并生成一個合適的 NSError 對象以向用戶顯示異常。

如果一個異常被拋出但并未被捕獲,那么默認(rèn)的未捕捉異常處理器(?uncaught exception handler )將會加載一個可以控制并終止應(yīng)用的消息。

你不應(yīng)該使用 try-block 塊來代替 ObjC 的標(biāo)準(zhǔn)程序檢測,以 NSArray 為例,你應(yīng)該總是先使用數(shù)組的數(shù)目( count )來確定元素的數(shù)量,然后才通過給定索引訪問一個對象。如果你發(fā)出了越界訪問請求,那么 objectAtIndex: 方法將會拋出一個異常,這樣你就可以在早期的開發(fā)過程中及時發(fā)現(xiàn) bug ——你應(yīng)該盡量避免在你已經(jīng)上線的 APP 中出現(xiàn)異常拋出。 更多關(guān)于 ObjC 應(yīng)用中的異常,參看?Exception Programming Topics

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號