在Cocoa应用程序中使用异常处理与NSError

11 error-handling cocoa memory-management objective-c try-catch-finally

大家好.我一直在阅读Apple关于何时/何地/如何使用NSError与@ try/@ catch/@的建议.从本质上讲,我的印象是Apple认为最好避免使用异常处理语言结构,除非作为在意外错误情况下暂停程序执行的机制(也许有人可以举例说明这种情况?)

我来自Java,当有人想要处理错误时,会有例外.不可否认,我仍然在Java思想空间,但我正在慢慢掌握NSError所提供的所有内容.

我挂断的一件事是在发生错误时清理内存的任务.在许多情况下(例如使用C,C++库,CoreFoundation等),您需要在由于错误而导致函数中断之前进行大量内存清理.

这是我煮熟的一个例子,它准确地反映了我遇到过的情况.使用一些虚构的数据结构,该函数打开一个文件句柄并创建一个"MyFileRefInfo"对象,其中包含有关如何处理该文件的信息.在关闭文件句柄并释放struct的内存之前,对文件执行了一些操作.使用Apple的建议我有这个方法:

- (BOOL)doSomeThingsWithFile:(NSURL *)filePath error:(NSError **)error
{
  MyFileReference inFile; // Lets say this is a CF struct that opens a file reference
  MyFileRefInfo *fileInfo = new MyFileRefInfo(...some init parameters...);

  OSStatus err = OpenFileReference((CFURLRef)filePath ,&inFile);

  if(err != NoErr)
  {
    *error = [NSError errorWithDomain:@"myDomain" code:99 userInfo:nil];
    delete fileInfo;
    return NO;
  }

  err = DoSomeStuffWithTheFileAndInfo(inFile,fileInfo);

  if(err != NoErr)
  {
    *error = [NSError errorWithDomain:@"myDomain" code:100 userInfo:nil];
    CloseFileHandle(inFile); // if we don't do this bad things happen
    delete fileInfo;
    return NO;
  }      

  err = DoSomeOtherStuffWithTheFile(inFile,fileInfo);

  if(err != NoErr)
  {
    *error = [NSError errorWithDomain:@"myDomain" code:101 userInfo:nil];
    CloseFileHandle(inFile); // if we don't do this bad things happen
    delete fileInfo;
    return NO;
  }      

  CloseFileHandle(inFile);
  delete fileInfo;
  return YES;

}
Run Code Online (Sandbox Code Playgroud)

现在..我的Java逻辑告诉我,最好将它设置为try/catch/finally结构,并将所有调用关闭文件句柄并释放finally块中的内存.

像这样......

    ...

    @try
    {
      OSStatus err = OpenFileReference((CFURLRef)filePath ,&inFile);
      if(err != NoErr)
      {
        ... throw some exception complete with error code and description ...
      }

      err = DoSomeStuffWithTheFileAndInfo(inFile,fileInfo);

      if(err != NoErr)
      {
         ... throw some exception ...
      }

      ... etc ...        
}
@catch(MyException *ex)
{
        *error = [NSError errorWithDomain:@"myDomain" code:[ex errorCode] userInfo:nil];
        return NO;
}
@finally
{
        CloseFileHandle(inFile); // if we don't do this bad things happen
        delete fileInfo;
}
return YES;
Run Code Online (Sandbox Code Playgroud)

我是不是觉得这是一个更优雅的解决方案,冗余代码更少?我错过了什么?

bbu*_*bum 17

丹尼尔的答案是正确的,但这个问题应该得到一个更直率的答案.

仅在遇到不可恢复的错误时抛出异常.

在传达可能从中恢复的错误情况时使用NSError.

在Apple框架中通过框架抛出的任何异常都可能导致未定义的行为.

开发中心提供了一个Exceptions编程主题文档.


Dan*_*sky 12

从本质上讲,我的印象是Apple认为最好避免使用异常处理语言结构,除非作为在意外错误情况下暂停程序执行的机制(也许有人可以举例说明这种情况?)

这不是我的印象.我认为Apple建议使用异常来实现真正的异常条件,并将NSError用于预期的失败.既然你来自Java,我认为NSError - > java.lang.Exception和Obj-C Exceptions - > java.lang.RuntimeException.当程序员做错了(例如,错误地使用API​​)时使用Obj-C异常,并在发生预期故障时使用NSError(例如,找不到远程服务器).

当然,这只是我对Apple的立场的解释.另一方面,我喜欢异常!

  • 你已经正确地抓住了Apple的位置.错误是您可能希望向用户呈现的可预见问题.例外应该永远不会发生或永远被抓住.Cocoa就是围绕这个设计的,例如AppKit的新错误呈现系统.http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/ErrorHandlingCocoa/ErrorHandling/ErrorHandling.html http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/Exceptions/Exceptions html的 (2认同)

小智 3

Objective-C 中的异常在历史上一直是“严重的”,进入 try 块会带来性能成本、抛出成本、使用finally 的成本等等。因此,Cocoa 开发人员通常会避免“哦不,”之外的异常。 “天塌下来了”的各种情况——如果文件丢失,则使用 NSError,但如果没有文件系统且可用内存量为负,则例外。

这就是历史的观点。但是,如果您在 10.5 或更高版本上构建 64 位应用程序,则异常架构已被重写为“零成本”,这可能意味着历史视图不再相关。与任何事情一样,这取决于多种因素 - 一种工作方式是否对您来说更自然并且可以让您更快地完成,如果您没有遇到任何与性能相关的问题,以及如果稍微与“传统”Objective-C 代码不一致不会打扰您...那么就没有理由不使用异常。