在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处理。 (我可能误解的理解来自this和this)
之类的东西我想知道NSOperations是否存在特殊情况,其中异常处理很重要,或者这是否是该指南特定作者的首选风格。
作为旁注,一些NSOperation示例代码遵循此样式,其他示例则不遵循此样式。大多数高可见性OSS不使用例外(例如AFNetworking)。
答案 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 **模式是其中两个。