异常:程序缺陷导致;不可恢复;给开发者使用;

错误:资源受限导致;可恢复;提示给用户。

https://blog.jayway.com/2010/10/13/exceptions-and-errors-on-ios/

Cocoa, and by inheritance Cocoa Touch on iOS makes a clear distinction between what is an exception, and what is an error in your application. This should be obvious since NSException and NSError both inherit from the NSObject root class with no relations at all.

Programmer vs. User

Exceptions are intended for signaling programming errors and fatal errors that the application can never recover from. One such error is index out of bounds when accessing an array. There is little reason to catch any exception; if you for example could not calculate a legal array index the first time around your app is in an illegal state. Exceptions are for your own use as a developer, to catch all programming errors in testing before shipping to the end user. Only secondary to inform the user of fatal errors when possible.

Errors are intended for signaling user errors, and conditions that can not be predicted until the end user runs your application. One such error is network timeout. There are many reasons to catch these errors; e.g. the user could be asked to check the network connectivity, and try again. Errors should always be handled, and be properly presented to the user. A user is much more forgiving if your application fails with a clear reason, than if it simply crashes or refuses to work.

Signal and Handle Exceptions

Exceptions are thown in Objective-C, just as in most other programming languages. You can construct your NSException object manually, and throw it using the @throw compiler directive if you like. But the preferred way is to use the NSExceptionconvenience class methods.

 
1
2
[NSException raise:NSInvalidArgumentException
            format:@"Foo must not be nil"];

Here we see the first divergence from how for example Java and C# handles exceptions. In Objective-C different exceptions are not normally signaled by different subclasses, but instead the exception is given a name, in the form of a constant string. Exceptions are however handled quite traditionally.

 
1
2
3
4
5
6
7
8
9
@try {
    // Normal code flow, with potential exceptions
}
@catch (NSException* e) {
    // Handle exception or re-throw
}
@finally {
    // Mandatory cleanup
}

Signal and Handle Errors

Errors do not use any special feature of the Objective-C run-time or programming language. Errors are instead treated as normal method arguments.

For synchronous tasks the error is returned as the last out argument of the method. A typical synchronous method signature with error handling looks like this:

 
1
2
- (id)initWithContentsOfURL:(NSURL *)url
                      error:(NSError **)outError

The client of such a task can always pass NULL to explicitly ignore the details of the error, the method must therefor also signal the error by the return value. This is typically done by returning nil, or NO if no other return value should exist. Ignoring the error is never recommended.

For asynchronous tasks the error is returned as the last argument of a delegate method. A typical asynchronous method signature with error handling looks like this:

 
1
2
- (void)connection:(NSURLConnection *)connection
  didFailWithError:(NSError *)error

The client of such a task can never opt out of receiving the error. It is still never recommended to ignore the error.

The information in an NSError is always localized and phrased in end-user friendly words. This is something you can trust of errors from system frameworks, and a hard requirement on you when you create your own errors. Therefor handling the error is never a burden, in the simplest case you can simply display the localized error message to the end user and call it a day.

 
1
2
3
4
5
6
7
8
9
- (void)connection:(NSURLConnection *)connection
  didFailWithError:(NSError *)error
{
[[[[UIAlertView alloc] initWithTitle:[error localizedDescription]
                                 message:[error localizedFailureReason]
                                delegate:nil
                       cancelButtonTitle:NSLocalizedString(@"OK", nil)
                       otherButtonTitles:nil] autorelease] show];
}

There is more to NSError

Cocoa and Cocoa Touch have much in common. Classes not brought straight over from Mac OS X to iOS often have a sibling anyway. One such class is NSAlert from Mac OS X and it’s sibling UIAlertView on iOS. They serve the same purpose of displaying an alert message to the end user, optionally with a few choices in the form of buttons.

One convenience method for handling errors on Mac OS X is -[NSAlert alertWithError:], it did not survive the transformation to UIAlertView on iOS. It is a pity, since it is a convenient way to quickly setup an alert with all the localized and human readable information. Basically turning the example code for handling an asynchronous error above from five lines of code, into a single line of code.

But that is not all. NSAlert and NSError on Mac OS X also have standardize facilities for handling recovery options. For example adding a “Retry” button to the alert, and calling the correct methods in response to this user selection.

On iOS NSError still have all the facilities to handle error recoveries, it is only UIAlertView that is lacking the final user facing bits. Fortunately this is easy to add. NSError manages error recovery with information held in it’s userInfo dictionary. The following keys are used:

  • NSLocalizedRecoverySuggestionErrorKey – A localized text with a general suggestion for how to recover from the error, for example “Check the network connection”.
  • NSLocalizedRecoveryOptionsErrorKey – An array of localized button titles such as “Retry”.
  • NSRecoveryAttempterErrorKey – An object conforming to the informal protocol NSErrorRecoveryAttempting.

The informal protocol NSErrorRecoveryAttempting declares one method that is significant for iOS:

 
1
2
- (BOOL)attemptRecoveryFromError:(NSError *)error
                     optionIndex:(NSUInteger)recoveryOptionIndex

The recoveryOptionIndex is the index into the array of button titles, and is the actual choice the user made for recovering from the error.

Adding a -[UIAlertView alertWithError:] convenience method to UIAlertView is made really easy since Objective-C has categories, and classes themselves are object instances so we can use the UIAlertView class as alert delegate for error recovery alerts.

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
@implementation UIAlertView (CWErrorHandler)
 
static NSMutableDictionary* cw_recoveryErrors = nil;
 
+(void)alertViewCancel:(UIAlertView *)alertView;
{
    NSValue* key = [NSValue valueWithPointer:(const void *)alertView];
    [cw_recoveryErrors removeObjectForKey:key];
}
 
+ (void)alertView:(UIAlertView *)alertView
clickedButtonAtIndex:(NSInteger)buttonIndex;
{
    NSValue* key = [NSValue valueWithPointer:(const void *)alertView];
    NSError* error = [cw_recoveryErrors objectForKey:key];
NSString* buttonTitle = [alertView buttonTitleAtIndex:buttonIndex];
    NSInteger recoveryIndex = [[error localizedRecoveryOptions]
                                   indexOfObject:buttonTitle];
    if (recoveryIndex != NSNotFound) {
    [[error recoveryAttempter]
             attemptRecoveryFromError:error
                          optionIndex:recoveryIndex];
    }
    [cw_recoveryErrors removeObjectForKey:key];
}
 
+(UIAlertView*)alertViewWithError:(NSError*)error;
{
    UIAlertView* alert = [UIAlertView alloc];
    [[alert initWithTitle:[error localizedDescription]
                  message:[error localizedFailureReason]
                 delegate:nil
        cancelButtonTitle:NSLocalizedString(@"Cancel", nil)
        otherButtonTitles:nil] autorelease];
    if ([error recoveryAttempter]) {
     if (cw_recoveryErrors == nil) {
cw_recoveryErrors = [[NSMutableDictionary alloc]
                                     initWithCapacity:4];
        }
        NSValue* key = [NSValue valueWithPointer:(const void *)alertView];
 
        [cw_recoveryErrors setObject:error
                              forKey:key];
        for (id recoveryOption in [error localizedRecoveryOptions]) {
         [alert addButtonWithTitle:recoveryOption];
        }
        alert.delegate = (id)self;
    }
    return alert;
}
 
@end

Conclusion

The clean separation of exceptions and errors help you as a developer to catch programming errors during development and with unit tests, and also handle errors of interest to the end users in a standardized and uniform way. The very nature of NSErrorencourages developers to write descriptive error messages that end users can understand and make informed discussions about. Errors are unavoidable in any application of non-neglectable complexity, gracefully handling these errors gives an aura of quality and ensures happy users. Happy users means better sales.

Full source code to the examples in this post, including some more convenience methods can be downloaded here, and are released under the Apache 2 open source license.

Exceptions and Errors on iOS的更多相关文章

  1. Exceptions & Errors - 异常与错误

    来源于 Ry’s Objective-C Tutorial - RyPress 一个学习Objective-C基础知识的网站. 个人觉得很棒,所以决定抽时间把章节翻译一下. 本人的英语水平有限,有让大 ...

  2. ruby Errors & Exceptions

    When you first started coding, errors were probably the last thing you wanted to see. After all, it’ ...

  3. Python Tutorial 学习(八)--Errors and Exceptions

    Python Tutorial 学习(八)--Errors and Exceptions恢复 Errors and Exceptions 错误与异常 此前,我们还没有开始着眼于错误信息.不过如果你是一 ...

  4. Handling Errors and Exceptions

    http://delphi.about.com/od/objectpascalide/a/errorexception.htm Unfortunately, building applications ...

  5. 笔记-python-tutorial-8.errors and exceptions

    笔记-python-tutorial-8.errors and exceptions 1.      errors and exceptions 1.1.    syntax errors >& ...

  6. 使用ab对nginx进行压力测试

    nginx以高并发,省内存著称. 相信大多数安装nginx的同学都想知道自己的nginx性能如何. 我想跟大家分享下我使用ab工具的压力测试方法和结果, ab是针对apache的性能测试工具,可以只安 ...

  7. Java Servlet规范

    截自网址:http://blog.csdn.net/u010391029/article/details/46521051 JavaServlet Specification  Version 2.3 ...

  8. CI框架使用PHPmail插件发送QQ邮件:

    有助请顶,不好请评.0:33 2016/3/12CI框架使用PHPmail插件发送QQ邮件:发送成功,不过修改了主机参数,还包含了一个phpmail中的一个另外的文件,详见下方:参见:http://c ...

  9. 关于搭建一个高性能网站的服务器的架设思路以及nginx测试的并发

    对于高性能网站的架设,主要就是请求量大,那我们该如何进行支撑? 考虑到下面的几个方面: 1.要减少请求,那对于开发人员来说,网站的css文件进行合并,背景图片也要合并,一般都是请求一张比较大的图片,然 ...

随机推荐

  1. Ruby中如何识别13位的时间戳

    由于13位的时间戳在Ruby中是比较另类的,以为Ruby中默认的时间戳都是10位的.而Time和Date是Ruby中常用的处理时间的模块. 由于最初遇到问题的时候网上搜了好久都没找到合适的,因此就自己 ...

  2. Idea提示没有符号类错误解决

    将提示没有符号类的文件打开,右键单独编译一次,再重新打包即可解决

  3. Windows 在目录中搜索哪个文件中有指定字符串

    findstr /s /i "string" *.* 表示,当前目录以及子目录下的所有文件中查找"string"这个字符串. *.*表示所有类型的文件. /s ...

  4. 洛谷P2846 光开关Light Switching

    题目描述 灯是由高科技--外星人鼠标操控的.你只要左击两个灯所连的鼠标, 这两个灯,以及之间的灯都会由暗变亮,或由亮变暗.右击两个灯所连的鼠 标,你就可以知道这两个灯,以及之间的灯有多少灯是亮的.起初 ...

  5. 记录一下今天犯得错误,public static function init()写成了public function initialize()

    tp5模型事件是放在函数 public static function init() 而我写成了初始化函数,编辑器生成的 public function initialize() 开始时用着没出问题, ...

  6. atom快捷键

    文件切换 ctrl-shift-s 保存所有打开的文件 cmd-shift-o 打开目录 cmd-\ 显示或隐藏目录树 ctrl-0 焦点移到目录树 目录树下,使用a,m,delete来增加,修改和删 ...

  7. vue中v-bind绑定样式

    近来发现v-bind绑定样式的两个好玩的栗子 可以直接绑定到一个样式对象,让模板更清晰: <div id="app"> <div v-bind:style=&qu ...

  8. codeforces C. Vasya And The Mushrooms (思维+模拟)

    题意:给定一个2*n的矩形方格,每个格子有一个权值,从(0,0)开始出发,要求遍历完整个网格(不能重复走一个格子),求最大权值和,(权值和是按照step*w累加,step步数从0开始). 转载: 题解 ...

  9. js获取文件MD5值

    原文链接:http://www.jianshu.com/p/940a9226fbbd   要在web页面中计算文件的md5值,还好这个项目是只需兼容现代浏览器的,不然要坑死了. 其实对文件进行md5, ...

  10. UnityError 切换场景灯光变黑问题解决

    编辑器在当前场景时,它的灯光是已经渲染好了,但重新加载的时候灯光没有进行渲染 解决方法如下: Window>>lighting>>Scene面板>>右下角build ...