为什么NSOperation示例代码使用@try& @抓住

时间:2012-12-02 16:06:47

标签: objective-c exception nsoperation

在Apple的并发编程指南中,NSOperation子类示例(非并发和并发变量)使用异常处理,我想知道为什么他们在操作中鼓励这种风格。

清单2-4响应取消请求

- (void)main {
   @try {
      BOOL isDone = NO;

      while (![self isCancelled] && !isDone) {
          // Do some work and set isDone to YES when finished
      }
   }
   @catch(...) {
      // Do not rethrow exceptions.
   }
}

我的理解是,通常异常处理不是Objective-C代码中的常见做法 - 异常本质上是程序员错误,应该导致应用程序崩溃,而意外输入最好由NSError处理。 (我可能误解的理解来自thisthis

之类的东西

我想知道NSOperations是否存在特殊情况,其中异常处理很重要,或者这是否是该指南特定作者的首选风格。

作为旁注,一些NSOperation示例代码遵循此样式,其他示例则不遵循此样式。大多数高可见性OSS不使用例外(例如AFNetworking)。

2 个答案:

答案 0 :(得分:8)

您的理解是正确的 - 应使用NSError(或类似)来传达错误信息,而不是异常。大多数Objective-C代码都不是异常安全的,至少会泄漏资源。作为一般规则,永远不要让您的代码泄露任何其他人的代码 - 无论是苹果公司还是第三方。一些第三方框架可能明确表明它们是异常安全的,但这种情况很少见。

根据该原则,您可以了解为什么在main方法中应该拥有一个包含所有异常处理程序的原因。但实际上还有另一个原因:您的操作将在专用线程上运行。从您的操作中抛出的异常将在堆栈中向上传播,但不会再进一步​​。操作的逻辑调用者或所有者将不会获取它们,因为它们在不同的线程上运行(或根本不运行)。因此,泄露的异常会杀死整个程序,或者在没有其他指示的情况下静默吞咽。您的程序可能会陷入奇怪的状态 - 因为您没有意识到发生了错误,您可能会继续等待您的操作结果永远不会到达。

此外,Apple在Concurrency Programming Guide中有一个部分,他们在那里讨论Handling Errors and Exceptions。他们关于“离散实体”的第一点是暗示我在上一段所说的内容:

  

处理错误和例外

     

因为操作本质上是   应用程序中的离散实体,他们负责   处理出现的任何错误或异常。在OS X v10.6及更高版本中,   NSOperation类提供的默认启动方法没有   捕捉异常。 (在OS X v10.5中,start方法确实捕获并且   抑制异常。)你自己的代码应该总是捕获和抑制   直接例外。它还应该检查错误代码并通知   根据需要提供适当的应用部分。如果你更换   start方法,你必须同样捕获你的任何异常   自定义实现,以防止他们离开范围   潜在的线程。

     

在您应该准备处理的错误情况类型中   如下:

     
      
  • 检查并处理UNIX错误样式的错误代码。
  •   
  • 检查显式错误   方法和函数返回的代码。
  •   
  • 抓住引发的异常   您自己的代码或其他系统框架。
  •   
  • 抓住异常抛出   由NSOperation类本身引发的异常   以下情况:      
        
    • 当操作尚未准备好执行时   它的start方法叫做
    •   
    • 当操作正在执行或完成时   (可能是因为它被取消了)并调用了它的start方法   再次
    •   
    • 当您尝试将完成块添加到操作时   已经执行或已完成
    •   
    • 当您尝试检索结果时   已取消的NSInvocationOperation对象
    •   
  •   
     

如果您的自定义代码   确实遇到异常或错误,你应该采取任何步骤   需要将该错误传播到您的应用程序的其余部分。   NSOperation类不提供传递的显式方法   错误结果代码或您的其他部分的例外   应用。因此,如果此类信息对您而言非常重要   应用程序,您必须提供必要的代码。

答案 1 :(得分:3)

我认为this post和随附的答案很好地阐述了一般异常与无异常处理主题!

  

在资源存在的情况下抛出异常是不安全的   不自动管理。这就是Cocoa框架的情况   (和邻居框架),因为他们使用手动引用计数。

     

如果您抛出异常,则通过展开跳过任何释放调用   堆栈将导致泄漏。这应该只限制你的时间   如果你确定你不会因为所有资源而恢复   进程退出时返回操作系统。

     

不幸的是,NSRunLoops倾向于捕获所有传播的异常   对他们来说,如果你在活动期间扔,你会恢复到下一个   事件。这显然是非常糟糕的。因此,你最好   只是不要扔。

     

如果您使用垃圾收集的Objective-C,这个问题会减少,   因为Objective-C对象所代表的任何资源都是正确的   释放。但是,C资源(例如文件描述符或   未包装在Objective-C对象中的malloc分配的内存   仍然会泄漏。

     

所以,总而言之,不要扔掉。

     

正如您所提到的,Cocoa API有几种解决方法。   返回nil和NSError **模式是其中两个。