3

我对 iPhone 中的异常处理有一些疑问。他们是这样的:

  1. 假设我有一个方法链一个接一个地被调用,也就是说,方法 A 调用方法 B,然后方法 C 又调用方法 D。这是放置我的 try-catch 块的最佳位置(是方法 A 还是 B 或 C 或 D 或全部)。此外,我需要向用户显示发生异常的警报,然后我想将此异常记录到我的服务器。所以,如果我在所有这些方法中编写我的 try-catch 块,并且方法 D 中发生异常;然后我认为警报将显示 4 次,并且用于记录的 Web 服务也将被调用 4 次(直到控制到达方法 A 的 catch 块)。那么,我应该只使用@throw;在方法 B、C 和 D 的 catch 块中,并在方法 A(顶级方法)的 catch 块中编写我的逻辑,或者我应该避免在方法 B、C 和 D 中编写 try-catch。

  2. 我需要异常中的某种错误代码(因为我的 Web 服务需要参数错误代码和描述)。是否可以将异常转换为错误,或者我需要对这段代码进行硬编码?

  3. 我在某处读过有关 NSSetUncaughtExceptionHandler 的信息。而且我认为,如果我可以设置此处理程序(在应用程序委托的 appDidFinishLaunching 方法中)和处理程序方法中,如果我向用户显示一些警报并调用 Web 服务;那么我不需要在我的每个方法中,在我的每个类中编写 try - catch 块。我对吗??

  4. 如果发生异常并且我编写了 try-catch 块或 NSSetUncaughtExceptionHandler,那么我的应用程序将继续运行还是不会响应任何用户事件。(我确信它会处理崩溃。我想知道它是否会挂起)

有人请告诉我这个例外主题。

4

1 回答 1

19

0) 避免 Cocoa 中的异常。它们通常是不可恢复的。您可能会为了自己的错误报告而捕获它们,但假设您可以从它们中恢复通常是不安全的。

1)如需抓,立即抓。不要编写自己的 throws - 相反,将其转换为类似 anNSError并传递它。NSError可以包含显示或发送错误代码以及本地化消息所需的所有信息。

2)您不能(直接)将 an 转换NSException为 an ,因为 an不具有 an 的所有属性- 它是一种不同的数据表示形式。一方面,错误代码不可用。二、描述未本地化。您可以做的最好的事情是创建一个错误代码和域,然后使用您需要的属性并将其存储在. 这可能类似于以下内容:NSErrorNSExceptionNSErrorNSExceptionNSError

// 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)-您永远不需要编写自己的 throw。-[NSArray objectAtIndex:和“对象不响应选择器”是程序员错误的例子——它们应该被捕获,但程序应该被纠正。

于 2011-12-06T08:33:19.443 回答