9.错误处理Dealing-with-Errors

错误处理

几乎每一款应用都会遇到错误。其中一些错误会超出你的控制,比如用光了磁盘空间或丢失了网络连接。其中一些错误是可以恢复的,如无效的用户输入。并且,虽然所有的开发人员都在努力追求完美,但程序员偶尔也会出现错误。

如果你是来自其他平台和语言的开发者,你过去常常可能使用异常(exceptions)来处理大多数的错误。当你使用 Objective-C 编写代码时,异常(exceptions)仅针对于程序员的错误,如数组的访问越界或无效的方法参数。这些问题是在你应用程序启动之前,在进行测试的时候就应该找到并修复的。

所有其他错误都是NSError类实例化的表现。本章给出了使用NSError对象的简要介绍,包括如何使用框架的方法可能会失败并返回错误。需要更多信息,请参见Error Handling Programming Guide

使用NSError处理大多数错误

错误是许多应用程序生命周期中不可避免的一部分。如果你需要从远程Web服务器请求数据,可能会出现各种各样的问题,比如:

  • 没有网络连接
  • 远程服务器可能无法访问
  • 远程服务器可能无法返回你请求的信息
  • 你收到的数据可能不符合你的期望

遗憾的是,不可能为每一个可能遇见的问题做应急预案和解决方案。取而代之的是,你必须为错误作出计划并且知道怎样处理它们,并带来最好的用户体验。

一些代理方法会提示你错误

如果你正在实现一个框架类(framework class)代理对象(delegate object)用于执行某个特定的任务,如从远程Web服务器中下载信息,则通常会发现你至少需要实现一个与错误相关的方法。例如,NSURLConnectionDelegate协议,包括一个connection:didFailWithError:方法:

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

如果出现一个错误,这个代理方法会被调用并给你提供一个NSError对象来描述问题。

一个NSError对象包含一个数字错误代码(numeric error code)域(domain)描述(description),以及被封装在一个用户信息字典(user info dictionary)中的其他相关信息。

相较于要求每一个可能的错误都有一个独特的数字代码,Cocoa和Cocoa Touch的错误被分为域(domins)。比如,如果发生一个NSURLConnection的错误,这个connection:didFailWithError:方法将提供来自NSURLErrorDomain的一个错误。

这个错误对象还包含一个本地化描述(localized description),如“找不到指定的服务器”。

一些方法通过参数传递错误

一些CocoaCocoa Touch接口(API)通过参数传回错误。例如,你可能决定使用NSData的方法writeToURL:options:error:,将你从Web服务器接收到的数据通过写入磁盘保存起来。这个方法的最后一个参数是对NSError指针的引用:

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

在你调用此方法之前,你需要创建一个合适的指针,以便你可以传入它的地址:

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

如果出现了错误,这个writeToURL:...方法将会返回NO,并更新你的anyError指针指向错误的对象来描述问题。

当处理参数传递的错误时,通过测试方法的返回值来查看是否发生了错误非常重要,如上面所示。不要仅仅只是测试查看这个错误指针是否被设置为指向了一个错误。

提示:如果你对错误对象不敢兴趣,只需要给error:参数传NULL就行了。

如果可能的话尽量恢复或向用户显示错误

最好的用户体验就是让你的应用程序显而易见地从错误中恢复。如果你正在制作一个远程Web请求,比如,你可能会尝试使用不同的服务器重新发送请求。或者你可能需要在再次请求之前从用户获取更多信息,比如有效的用户名或密码凭据。

如果它不可能从错误中恢复,你应该提醒用户。如果你正在使用Cocoa TouchiOS开发,你需要创建和配置一个UIAlertView来显示错误。如果你在使用CocoaOS X(mac OS)开发,你可以在任何NSResponder对象(如视图、窗口或应用程序对象本身)调用presentError:并且错误会随响应链(responder chain)传递以寻求更多配置或恢复。当它到达应用程序对象时,应用程序通过一个警告面板向用户呈现错误。

更多有关向用户展现错误的信息,请参见 Displaying Information From Error Objects

创建你自己的错误对象

为了创建自己的NSError对象,你需要定义你自己的错误域(domain),它应该是这样的形式:

com.companyName.appOrFrameworkName.ErrorDomain

你还需要为每一个可能发生在你域(domain)中的错误选择一个唯一的错误代码,以及一个合适的描述,它被存储在用户信息字典(user info dictionary)中,像这样:

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

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

这个示例使用NSLocalizedString函数从一个Localizable.strings文件来查找错误描述的本地化版本,作为本地化字符串资源(Localizing String Resources)的描述。

如果你需要通过参数传回一个错误作为早期描述,你的方法签名(method signature)应该包含一个指向NSError对象的指针作为参数。你也应该使用返回值来表示成功或者失败,如:

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

如果出现错误,在你尝试引用它来设置错误之前,你首先应该检查是否给错误指针提供了一个非空指针,在返回NO来表示失败之前,像这样:

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

异常用来处理程序员的错误

Objective-C支持异常(exceptions),有和其他许多编程语言有相同的用法,和Java或C++有相似的语法。和NSError一样,在CocoaCocoa Touch中的异常都是对象,通过NSException类的实例来表示。

如果你需要编写可能引发异常的代码,你可以将该代码封装在一个try-catch代码块(block)中:

@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
}

如果一个异常被在@try代码块中的代码抛出,它将会被@catch代码块捕获,你便可以处理它。比如,如果你在使用用异常(exceptions)来处理错误的底层C++库,你可能会捕获到异常并创建合适的NSError对象来向用户显示。

如果一个异常被抛出但没有被捕获,默认的没有捕获异常处理会记录信息到控制台并终止程序。

你不应该在标准程序检查Objective-C方法的地方使用try-catch代码块。以一个NSArray为例,你应该在试图访问指定下标的对象之前,总是检查数组的count来确定元素的数量。如果你做了一个越界的访问请求,objectAtIndex:方法会抛出一个异常,在开发周期的早期你就可以找到在你代码中的漏洞(bug),你在给用户的应用中应该避免抛出异常。

想了解更多关于在Objective-C程序中异常的信息,请参考Exception Programming Topics

    原文作者:我真的是爆炸糖
    原文地址: https://www.jianshu.com/p/28a89f85b921
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞