【问题标题】:Exception handling in entire application整个应用程序中的异常处理
【发布时间】:2012-01-13 20:14:30
【问题描述】:

我对 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,那么我的应用程序将继续运行还是不会响应任何用户事件。 (我确信它会处理崩溃。我想知道它是否会挂起)

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

【问题讨论】:

    标签: iphone objective-c ios exception-handling nsexception


    【解决方案1】:

    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: 和“对象不响应选择器”是程序员错误的示例 - 它们应该被捕获,但程序应该被纠正。

    【讨论】:

    • 嗨贾斯汀,谢谢亲爱的信息。但是你能告诉我 NSSetUncaughtExceptionHandler 和 2 号的使用吗,如果发生异常,那么我应该中止应用程序或使其崩溃。我的意思是,我不应该允许用户继续使用它。另外,你能告诉我如何将 NSException 转换为 NSError。
    • 什么是 MON* 键?它们是在 Cocoa 中定义的吗?
    • @QED 不。它只是我在示例代码中使用的前缀(例如,而不是 NS、CF、CG 等前缀)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-01
    • 2014-05-04
    • 1970-01-01
    相关资源
    最近更新 更多