整个应用程序中的异常处理



我对iPhone的异常处理有一些疑问。以下是它们:

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

  2. 我需要一些来自异常的错误代码(因为我的web服务需要参数错误代码和描述)。是否有可能将异常转换为错误,或者我是否需要硬编码此代码?

  3. 我在某个地方读过NSSetUncaughtExceptionHandler。我认为,如果我能设置这个handler(在app委托的appDidFinishLaunching方法中)在handler方法中,如果我向用户显示一些警报并调用web服务;那么我就不需要在我的每个方法中,在我的每个类中编写try - catch块。我说的对吗?

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

谁来给我讲讲这个例外的话题。

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

1)如果你需要抓住它,立即抓住它不要编写自己的throw —相反,将其转换为类似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中编写异常的人,如果你想捕获它们,可能都没有很好地理解这个问题,无法实现一个可靠的unwind实现(例如,即使它产生泄漏,它也不是可靠的)。

3)那真的不是显示警告的时间和地点。如果您安装了一个顶级异常处理程序(通过NSSetUncaughtExceptionHandler)——您应该简单地记录一条消息——那么异常处理程序将中止。你的应用处于不稳定状态——继续比中止更糟糕。你可能想把这些自定义消息发送回家,最好在下次启动你的应用程序时这样做。

在大多数情况下,你的应用处于不稳定状态,你不应该继续。但是,要真正回答这些极端情况:"是的,您可以在捕获时恢复并继续,但是您应该只在抛出API声明支持恢复时尝试恢复并继续。"如果问题超出了你的控制范围,并且问题不是异常的(例如,未找到文件),并且供应商真的希望你继续,那么我不得不假设他们希望你继续,即使它真的不是(100%安全)。"不要试图从顶级异常处理程序中恢复/继续(程序返回后将中止)。如果你想在OSX上表现得非常出色,那么另一个进程将是最好的选择。如果您通过纯c++接口调用,那么unwind是定义良好的,并且需要捕获—如果它是可恢复的,请继续。c++中的异常可以恢复并且定义良好——它们也被广泛使用(包括少于异常的条件)。

(IMO…)ObjC中的异常不应该被引入,任何从系统或第三方库抛出的方法都应该被弃用。它们不能很好地展开,或者以一种明确的方式展开。同样,展开流与正常的Cocoa程序流相反。这意味着触摸任何对象的内存/关系,在抛出和捕获之间的抛出和未定义的行为的时间是在变化。问题是—您不知道内存是什么(在大多数情况下,并且在合理的维护时间内)。c++异常定义得很好,并且它们可以正确地展开(例如,调用析构函数)——但是试图在ObjC上下文中继续执行会忽略未定义行为的任何后果。在我看来,它们应该只存在于objc++中(因为c++需要它们)。

在理想情况下,您的ObjC程序和您使用的库不会使用异常(根本不会)。因为你使用的库会抛出(包括Cocoa),所以只有当你需要一些关于错误的特殊信息时,才安装一个顶级异常处理程序。如果API要求您可以预期由于超出您控制的情况而抛出异常,并且您希望恢复,那么编写一个catch,但立即将该逻辑转换为正常的程序流(例如NSError) -您永远不需要编写自己的throw。-[NSArray objectAtIndex:和"object does not response to selector"是程序员错误的例子——它们不应该被捕获,但是程序应该被纠正。

最新更新