NSError了解一哈

说来惭愧,工作以来一直没有好好了解NSError这个类。这次的话因为一些需求,准备系统的学习一下子,下面就是我大致的总结。

综述

OC中通常使用NSError对象来发出错误信号,提供错误类型以及任何潜在原因的额外信息。

Foundation和其它Cocoa框架产生的错误通常归属于NSCocoaErrorDomain错误域。NSCocoaErrorDomain中的错误状态码都是在Foundation定义好的常量。

在子类中,可以通过覆写localizedDescription方法来提供更好的本地错误描述。

NSErrorCFError的无缝转换对象。

每个NSError对象主要提供三部分的信息:

  • code 状态码
  • domain 对应的特定错误域
  • userInfo 额外的信息

code和domain

code状态码表示问题的本质,这些状态码都在一个特定的错误域中,以防重叠和混淆。

例如在NSCocoaErrorDomain中,NSFileManager访问一个不存在的文件产生的错误代码是4。而在NSPOSIXErrorDomain中,4代表中断函数错误。

系统错误域

userInfo及一些属性

作为Cocoa的惯例,userInfo是一个可以包含任意键值对的字典,无论是为了继承或降低耦合的目的, 它都不适合拿来填满各种杂七杂八的属性。在NSError这个例子中,有一些特定的键值对应着只读属性。一下是标准NSErroruserInfo的键列表

  • NSLocalizedDescriptionKey
  • NSLocalizedFailureReasonErrorKey
  • NSLocalizedRecoverySuggestionErrorKey
  • NSLocalizedRecoveryOptionsErrorKey
  • NSFilePathErrorKey
  • NSStringEncodingErrorKey
  • NSUnderlyingErrorKey
  • NSRecoveryAttempterErrorKey
  • NSHelpAnchorErrorKey

下面是常见的几个键:

localizedDescription

NSLocalizedDescriptionKey的对应值,即userInfo[NSLocalizedDescriptionKey],下同
一段本地化的错误描述。错误的主要可呈现信息。例如NSFileReadNoPermissionError:”文件XX无法打开,因为你并没有查看它的权限”。理想状况下,这个信息会指出失败的原因以及失败原因。这个值来自于NSLocalizedDescriptionKey或者是NSLocalizedFailureErrorKey + NSLocalizedFailureReasonErrorKey或者是NSLocalizedFailureErrorKey。构建描述信息的步骤如下:

  • userInfo中查找NSLocalizedDescriptionKey,如果存在则使用
  • userInfo中查找NSLocalizedFailureErrorKey,如果存在,则使用,与NSLocalizedFailureReasonErrorKey的值组合使用(如果存在)
  • userInfoValueProvider中查找NSLocalizedDescriptionKey,如果存在则使用
  • userInfoValueProvider中查找NSLocalizedFailureErrorKey,如果存在则使用,与NSLocalizedFailureReasonErrorKey的值组合使用(如果存在)
  • userInfo或者userInfoValueProvider查找NSLocalizedFailureErrorKey,如果存在则使用

上文中提到的userInfoValueProvider我也不知道是什么东西,我猜测可能是NSError的类方法+ (id _Nullable (^)(NSError * _Nonnull, NSErrorUserInfoKey _Nonnull))userInfoValueProviderForDomain:(NSErrorDomain)errorDomain; 中的返回值

localizedRecoverySuggestion

NSLocalizedRecoverySuggestionErrorKey的对应值,即userInfo[NSLocalizedRecoverySuggestionErrorKey],一段该错误的恢复建议,适合在alert中显示为辅助消息。

localizedFailureReason

localizedFailureReason的对应值,即userInfo[localizedFailureReason],一段本地化的错误解释

如何使用调用系统API时返回的NSError

了解过上面的知识之后,那我们要怎样更好的使用系统返回给我们的NSError呢,下面是我自己使用的方式:

1
2
3
4
5
6
7
8
9
10
11
12
- (void)showErrorDetail:(NSError *)error viewController:(UIViewController *)viewController
{
if (![viewController isKindOfClass:[UIViewController class]]) {
return;
}
if (![error isKindOfClass:[NSError class]] || !error) {
return;
}
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:error.localizedDescription message:error.localizedRecoverySuggestion preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"确定", nil) style:UIAlertActionStyleDefault handler:nil]];
[viewController presentViewController:alertController animated:YES completion:nil];
}

如何创建一个NSError

作为开发者,我们需要怎么样使用NSError,才能更好的传递错误信息呢?首先,我们可以按Foundation库中很多类那个样子,在一个自定义方法中定义NSError **类型的形参。然后,我们也可以定义属于自己的错误域、错误代码常量和userInfo中的Key。

1
2
3
4
5
6
7
8
9
10
// 自定义Domain
NSString *const JYCustomErrorDomain = @"JYCustomErrorDomain";
// 自定义key
NSString *const JYValidationStatusErrorKey = @"JYValidationStatusErrorKey";
// 自定义错误码
typedef NS_ENUM(NSInteger, JYCustomErrorCode)
{
JYCustomErrorCodeGen = -999,
JYCustomErrorCodeRequired = -1000
};

使用userInfo字典来创建一个NSError

1
2
3
4
5
6
7
8
NSDictionary *userInfo = @{
NSLocalizedDescriptionKey: NSLocalizedString(@"Operation was unsuccessful.", nil),
NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"The operation timed out.", nil),
NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Have you tried turning it off and on again?", nil)
};
NSError *error = [NSError errorWithDomain:JYCustomErrorDomain
code:JYCustomErrorCodeGen
userInfo:userInfo];

将错误信息包装在NSError对象中,很容易在不同对象或者上下文中进行传递。

结束

参考来源:

作者

千行

发布于

2019-03-13

更新于

2022-10-21

许可协议

评论