事實上,OC是具備異常處理機制的,但是具體情況下不會啟用該機制。
ARC默認不是“異常安全”的。如果拋出異常,那么本應該在作用域末尾釋放的對象在現在不能自動釋放了。如果想生成“異常安全”的代碼,需要設置編譯標志“-fobjc-arc-exceptions”,并且需要引入額外的代碼,在不拋出異常時,也照樣執行這段代碼。
即使不用ARC,也很難在拋出異常后不會導致內存泄露。
例如:
id resource = [YCResource alloc] init] retain];
if (/*有異常發生*/) {
@throw [NSException exceptionWithName:@"/*異常名稱*/" reason@"/*異常原因*/" userInfo:nil];
}
[resource callSomeMethod];
[resource release];
只有在極其罕見的情況下跑出異常,異常拋出之后,無需考慮恢復問題,而且應用程序此時也應該退出。
例如:OC中沒有抽象類,所以無法定義抽象方法,此時,如果你想定義一套抽象 的API,那么可以在方法的實現中拋出異常,告訴使用者需要覆寫該方法
- (void)absructMethd {
NSString exceptionReason = [NSString stringWithFormat:@"%@ must be overridden", NSStringFromSelector(_cmd)];
@throw [NSException exceptionWithName:NSInternalInnconsistencyException reson:exceptionReason userInfo:nil];
}
在出現"不那么嚴重的錯誤"(nonfatal error, 非致命錯誤)時,Objective-C語言所用的編程范式為: 令方法返回nil/0,或是使用NSError,以表明其中有錯誤發生。
OC的范式是:另方法返回0/nil,或者使用NSError。
NSError
NSErro使用起來非常靈活,我們使用它告訴調用者發生了什么錯誤。
//兩種初始化方法:其中,domain 不能為空 dict可以為空
- (instancetype)initWithDomain:(NSErrorDomain)domain code:(NSInteger)code userInfo:(nullable NSDictionary *)dict;
+ (instancetype)errorWithDomain:(NSErrorDomain)domain code:(NSInteger)code userInfo:(nullable NSDictionary *)dict;
@property (readonly, copy) NSErrorDomain domain;//錯誤域
@property (readonly) NSInteger code;//錯誤代碼
@property (readonly, copy) NSDictionary *userInfo;//錯誤信息
@property (readonly, copy) NSString *localizedDescription;//獲取本地化描述
@property (nullable, readonly, copy) NSString *localizedFailureReason;//獲取失敗原因
@property (nullable, readonly, copy) NSString *localizedRecoverySuggestion;//獲取恢復建議
@property (nullable, readonly, copy) NSArray<NSString *> *localizedRecoveryOptions;本地恢復建議
@property (nullable, readonly, strong) id recoveryAttempter;
@property (nullable, readonly, copy) NSString *helpAnchor;
NSError構成
-
Error domain(錯誤范圍)
- 描述錯誤發生的范圍。例如處理URL的子系統,當url解析出現錯誤,就可以使用NSURLErrorDomain來表示錯誤范圍。
- 錯誤域主要有四個,對于Carbon框架的Error,歸于OSStatus domain(NSOSStatusErrorDomain),對于POSIX error,歸于NSPOSIXErrorDomain,而對于我們的iOS開發,一般使用NSCocoaErrorDomain。NSError.h定義了四個domain。
{ // Predefined domain for errors from most AppKit and Foundation APIs. FOUNDATION_EXPORT NSErrorDomain const NSCocoaErrorDomain; // Other predefined domains; value of "code" will correspond to preexisting values in these domains. FOUNDATION_EXPORT NSErrorDomain const NSPOSIXErrorDomain; FOUNDATION_EXPORT NSErrorDomain const NSOSStatusErrorDomain; FOUNDATION_EXPORT NSErrorDomain const NSMachErrorDomain; }
Error code(錯誤碼)
獨有的錯誤代碼,用以表明具體發生了何種錯誤,一般用枚舉定義,HTTP請求中可以存儲狀態碼。
Foundation Constants Reference
NSError Codes
NSError codes in the Cocoa error domain.
enum {
NSFileNoSuchFileError = 4,
NSFileLockingError = 255,
NSFileReadUnknownError = 256,
NSFileReadNoPermissionError = 257,
NSFileReadInvalidFileNameError = 258,
NSFileReadCorruptFileError = 259,
NSFileReadNoSuchFileError = 260,
NSFileReadInapplicableStringEncodingError = 261,
NSFileReadUnsupportedSchemeError = 262,
NSFileReadTooLargeError = 263,
NSFileReadUnknownStringEncodingError = 264,
NSFileWriteUnknownError = 512,
NSFileWriteNoPermissionError = 513,
NSFileWriteInvalidFileNameError = 514,
NSFileWriteInapplicableStringEncodingError = 517,
NSFileWriteUnsupportedSchemeError = 518,
NSFileWriteOutOfSpaceError = 640,
NSFileWriteVolumeReadOnlyError = 642m
NSKeyValueValidationError = 1024,
NSFormattingError = 2048,
NSUserCancelledError = 3072,
NSFileErrorMinimum = 0,
NSFileErrorMaximum = 1023,
NSValidationErrorMinimum = 1024,
NSValidationErrorMaximum = 2047,
NSFormattingErrorMinimum = 2048,
NSFormattingErrorMaximum = 2559,
NSPropertyListReadCorruptError = 3840,
NSPropertyListReadUnknownVersionError = 3841,
NSPropertyListReadStreamError = 3842,
NSPropertyListWriteStreamError = 3851,
NSPropertyListErrorMinimum = 3840,
NSPropertyListErrorMaximum = 4095
NSExecutableErrorMinimum = 3584,
NSExecutableNotLoadableError = 3584,
NSExecutableArchitectureMismatchError = 3585,
NSExecutableRuntimeMismatchError = 3586,
NSExecutableLoadError = 3587,
NSExecutableLinkError = 3588,
NSExecutableErrorMaximum = 3839,
}
URL Loading System Error Codes
These values are returned as the error code property of an NSError object with the domain “NSURLErrorDomain”.
typedef enum
{
NSURLErrorUnknown = -1,
NSURLErrorCancelled = -999,
NSURLErrorBadURL = -1000,
NSURLErrorTimedOut = -1001,
NSURLErrorUnsupportedURL = -1002,
NSURLErrorCannotFindHost = -1003,
NSURLErrorCannotConnectToHost = -1004,
NSURLErrorDataLengthExceedsMaximum = -1103,
NSURLErrorNetworkConnectionLost = -1005,
NSURLErrorDNSLookupFailed = -1006,
NSURLErrorHTTPTooManyRedirects = -1007,
NSURLErrorResourceUnavailable = -1008,
NSURLErrorNotConnectedToInternet = -1009,
NSURLErrorRedirectToNonExistentLocation = -1010,
NSURLErrorBadServerResponse = -1011,
NSURLErrorUserCancelledAuthentication = -1012,
NSURLErrorUserAuthenticationRequired = -1013,
NSURLErrorZeroByteResource = -1014,
NSURLErrorCannotDecodeRawData = -1015,
NSURLErrorCannotDecodeContentData = -1016,
NSURLErrorCannotParseResponse = -1017,
NSURLErrorFileDoesNotExist = -1100,
NSURLErrorFileIsDirectory = -1101,
NSURLErrorNoPermissionsToReadFile = -1102,
NSURLErrorSecureConnectionFailed = -1200,
NSURLErrorServerCertificateHasBadDate = -1201,
NSURLErrorServerCertificateUntrusted = -1202,
NSURLErrorServerCertificateHasUnknownRoot = -1203,
NSURLErrorServerCertificateNotYetValid = -1204,
NSURLErrorClientCertificateRejected = -1205,
NSURLErrorClientCertificateRequired = -1206,
NSURLErrorCannotLoadFromNetwork = -2000,
NSURLErrorCannotCreateFile = -3000,
NSURLErrorCannotOpenFile = -3001,
NSURLErrorCannotCloseFile = -3002,
NSURLErrorCannotWriteToFile = -3003,
NSURLErrorCannotRemoveFile = -3004,
NSURLErrorCannotMoveFile = -3005,
NSURLErrorDownloadDecodingFailedMidStream = -3006,
NSURLErrorDownloadDecodingFailedToComplete = -3007
}
-
User info(用戶信息)
- 有關錯誤的額外附加信息,可以是一段本地化描述,也可能是用來存儲該錯誤是由其他錯誤引起的,最終生成一條完整的錯誤鏈。
- 預定義的userinfo鍵名
NSString *const NSUnderlyingErrorKey;//推薦的標準方式,通用鍵 NSString *const NSLocalizedDescriptionKey; // 詳細描述鍵 NSString *const NSLocalizedFailureReasonErrorKey; // 失敗原因鍵 NSString *const NSLocalizedRecoverySuggestionErrorKey; //恢復建議鍵 NSString *const NSLocalizedRecoveryOptionsErrorKey; // 恢復選項鍵
- 其他鍵
NSString *const NSRecoveryAttempterErrorKey; NSString *const NSHelpAnchorErrorKey; NSString *const NSStringEncodingErrorKey ; NSString *const NSURLErrorKey; NSString *const NSFilePathErrorKey;
NSError應用場景類型
- 在代理協議中返回NSError錯誤
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
- 在方法中用對象指針來監測錯誤
- (BOOL)doSomething:(NSError *)error;
/*使用方法*/
NSError *error = nil;
BOOL ret = [slef doSomething:&error];
/**
因為 需要將error 傳入后修改其值,然后再返回來,返回來后還要保證己經修改過了。
&error傳入是傳的地址引用,傳入后處理函數直接訪問變量的地址,可以修改其值再返回同一個地址, 調用函數就可以知道值是否有修改,即是否有錯。
而error傳入是傳的值引用,值引用傳入到程序棧中后其實是把原來的值復制了一份傳過去,處理函數可以修改,但無法將改后的值傳出函數體。
*/
if (ret) {
// 處理錯誤
}
要點
只有發生了可使整個應用程序崩潰的嚴重錯誤時,才應使用異常。
在錯誤不那么嚴重的情況下,可以指派“委托方法”(delegate method)來處理錯誤,也可以把錯誤信息放在NSError對象里,經由“輸出參數”返回給開發者。