# 21、理解Objective-C错误模型

`Error`对象里封装了三条信息：

* Error domain：错误范围，字符串

  产生错误的根源，通常用一个特有的全局变量来定义。如：`NSURLError`表示解析URL出错
* Error code：错误码，整数

  独有的错误码，指明在某个范围内具体发生了何种错误，通常用enum定义。如：HTTP请求出错时，可能回把HTTP的状态码设为错误码
* User info：用户信息，字典

  有关此错误的额外信息，其中或许包含一段“本地化的描述”（localized description），或许还含有导致该错误发生的另外一个错误，经由此种信息，可将相关错误串成一条“错误链”（chain of errors）
* 通过“委托协议”传递错误

  有错误放生时，当前对象会把错误信息经由协议中的某个方法传递给其`delegate`委托对象，如：

  ​ 当`NSURLConnection`出错后（比如与远程服务器的链接操作超时了），就回调用此方法以处理相关错误：

  ```
  - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    DDLogDebug(@"connection didFailWithError %@", error);
  }
  ```

  ​ 这个委托方法未必非得实现不可：是不是必须处理此错误，可交由`NSURLConnection`类的用户来判断。（这比抛出异常要好，因为调用者至少可以自己决定是否需要处理该错误）
* 通过方法的“输出参数”传递错误

  如：

  ```
  // 传入的是一个指针的内存地址（因为change的是：指针重指向，而不是修改指向的内容）
  - (BOOL)doSomething:(NSError **)error {
    if (/* 有错误*/) {
      if (error) { // 必须判断 
        // *error: 为error参数“解引用”(dereference)，及error所指的那个指针现在要指向一个新的NSError对象了。
        *error = [NSError errorWithDomain:domain code:code userInfor:dic];
        // 在解引用之前必须保证error参数不是nil
        // 因为空指针解引用会导致carsh：“段错误”segmentation fault
      }
    }
  }
  // 使用：
  // 1.在乎error
  NSError *error = nil;
  BOOL result = [object doSomething:&error];
  if (error) {
    // 处理error
  }
  // 2.不在乎error
  BOOL result = [object doSomething:nil];
  if (!result) {
      // 处理error
  }
  ```

  像这样的方法一般返回Boolean值，表示该操作成功 or 失败。如果调用者不关注估计的错误信息，则直接判断该Boolean值就好了；若关注具体错误，那就检查经由“输出参数”所返回的那个错误对象。

​ NSError的domain、code、userInfor应该根据具体错误情况填入适当内容，方便调用者根据错误类型分别处理各错误。domain定义成`NSString`类型的全局常量，而code则定义成枚举类型为佳。如：

```objectivec
// XXXErrors.h
extern NSString *const XXXErrorDomain;
typedef NS_ENUM(NSUInteger, XXXError) {
  XXXErrorUnKnown             = -1,
  XXXErrorGeneralFault    = 100,
  XXXErrorBadInput            = 101,
}
// XXXErrors.m
NSString *const XXXErrorDomain = @"XXXErrorDomain";
```

建议：

* 为自己的程序库中所发生的错误制定一个专用的“错误范围”字符串
* 用枚举定义错误码，不仅解释错误码的含义，还给它们起了个有意义的名字

要点：

* 只有发生了会使整个应用程序崩溃的严重错误时，才使用异常
* 在错误不那么严重的情况下，可以指派“委托方法”（delegate method）来处理错误，也可以把错误信息放在`NSError`对象里，经由“输出参数”返回给调用者
