0) 避免 Cocoa 中的异常。它们通常是不可恢复的。您可能会为了自己的错误报告而捕获它们,但假设您可以从它们中恢复通常是不安全的。
1) 如果您需要捕捉,请立即捕捉。 不要编写自己的 throws - 相反,将其转换为 NSError 之类的东西并传递它。 NSError 可以包含显示或发送错误代码以及本地化消息所需的所有信息。
2) 您不能(直接)将NSException 转换为NSError,因为NSException 不具备NSError 的所有属性 - 它是一种不同的数据表示形式。一方面,错误代码不可用。二、描述未本地化。您可以做的最好的事情是创建一个错误代码和域,然后使用NSException 中所需的属性并将其存储在NSError 中。这可能类似于以下内容:
// error checking omitted
extern NSString* const MONExceptionHandlerDomain;
extern const int MONNSExceptionEncounteredErrorCode;
NSError * NewNSErrorFromException(NSException * exc) {
NSMutableDictionary * info = [NSMutableDictionary dictionary];
[info setValue:exc.name forKey:@"MONExceptionName"];
[info setValue:exc.reason forKey:@"MONExceptionReason"];
[info setValue:exc.callStackReturnAddresses forKey:@"MONExceptionCallStackReturnAddresses"];
[info setValue:exc.callStackSymbols forKey:@"MONExceptionCallStackSymbols"];
[info setValue:exc.userInfo forKey:@"MONExceptionUserInfo"];
return [[NSError alloc] initWithDomain:MONExceptionHandlerDomain code:MONNSExceptionEncounteredErrorCode userInfo:info];
}
@catch (NSException * exc) {
NSError * err = NewNSErrorFromException(exc);
...
}
如果您使用的 API 引发了您应该捕获并从中恢复的异常(例如,不是真正的异常情况),那么是的,您可以捕获并尝试继续。不幸的是,任何在 Cocoa 中编写异常以捕获它们的意图的人可能都没有充分理解问题以实现可靠的展开实现(例如,即使它产生泄漏,它也不是可靠的)。
3) 这真的不是显示警报的时间或地点。如果你安装了一个顶级异常处理程序(通过NSSetUncaughtExceptionHandler)——你应该简单地记录一条消息——然后异常处理程序将中止。您的应用程序处于不稳定状态 - 继续比中止更糟糕。您可能希望将这些自定义消息发送回家,最好在下次启动您的应用时这样做。
4) 在大多数情况下,您的应用处于不稳定状态,您不应继续。但是,要实际回答那些极端情况:“是的,您可以在捕获时恢复并继续,但您应该只在抛出 API 声明支持恢复时尝试恢复并继续。如果问题超出您的控制范围,并且问题并不例外(例如找不到文件),并且供应商确实希望您继续,那么我不得不假设他们希望您继续,即使它确实不是(100%安全)。”。不要尝试从顶级异常处理程序中恢复/继续(程序将在返回后中止)。如果您想非常漂亮并立即在 OSX 上呈现它,那么最好使用另一个过程。如果您通过纯 C++ 接口调用,则展开是明确定义的,并且需要捕获 - 如果它是可恢复的,请继续。 C++ 中的异常是可恢复的且定义明确的——它们也被广泛使用(包括不那么异常的条件)。
(IMO...) 不应在 ObjC 中引入异常,并且应弃用从系统或第三方库抛出的任何方法。它们不能很好地放松,也不能以明确的方式放松。同样,展开流与正常的 Cocoa 程序流相反。这意味着接触任何 objc 对象的内存/关系,这些内存/关系在投掷时处于突变状态并且位于投掷和捕获之间,与未定义的行为一样好。问题是 - 您不知道该内存是什么(在大多数情况下,并且在合理的维护时间内)。 C++ 异常定义明确,并且它们可以正确展开(例如调用析构函数) - 但尝试在 ObjC 上下文中继续会忽略未定义行为的任何后果。 IMO,它们应该只存在于 ObjC++(因为 C++ 需要它们)。
在理想情况下,您的 ObjC 程序和您使用的库(完全)不会使用异常。由于您使用会抛出异常的库(包括 Cocoa),因此仅在您需要有关错误的一些特殊信息时才安装顶级异常处理程序。如果 API 要求您可能期望抛出异常由于您无法控制的情况并且您应该恢复,则编写一个 catch 但立即将该逻辑转换为正常的程序流(例如NSError) - 你永远不需要写你自己的投掷。 -[NSArray objectAtIndex: 和“对象不响应选择器”是程序员错误的示例 - 它们应该不被捕获,但程序应该被纠正。