【问题标题】:What’s the rationale behind the Cocoa exception policy - or why use exceptions only for programmer errors?Cocoa 异常策略背后的基本原理是什么——或者为什么只对程序员错误使用异常?
【发布时间】:2010-09-28 10:00:34
【问题描述】:

Cocoa 异常政策背后的基本原理是什么 - 或者为什么只对程序员错误使用异常?

我知道异常过去是相当昂贵的,所以人们不想过度使用它们。但这随着现代运行时而改变,它是零成本的例外。我也明白使用异常来做一般控制流不是一个好主意,因为它可能导致代码相当难以理解。

但是为什么要使用异常来表示程序员的错误呢?对于这种情况,记录一条消息后跟abort() 就足够了。为什么我应该编写一个@catch(...) 块来处理程序员错误而不是修复实际错误?我一直在思考这个问题,但我没有找到任何合理使用异常来解决程序员错误的方法。

(作为旁注/问题:我已经编写了一个递归下降解析器,并且我计划在其中使用异常来处理错误。对我来说,这似乎比为每个单独添加一个 out 参数更合理在那里的函数并手动检查到处是否有错误。当然,我会捕获我在从外部调用的顶级方法中抛出的任何异常。有人认为这对异常来说是一个不好的用途吗?)

更新:真正的问题

感谢到目前为止的所有答案。他们都是真的,但他们并没有真正回答我的问题。所以我想我不是很清楚,对此感到抱歉。所以这是真正的问题:

为什么 Cocoa 会为程序员错误(或断言)抛出异常? 不应该捕获它们,实际上编写代码来处理调用堆栈中某处的程序员错误并不是反正是个好主意。在我看来,例外是浪费精力。只需记录错误并调用abort()(退出程序)就足够了。那么实际抛出异常有什么好处呢?

我理解为什么通常不使用和不鼓励异常 - Cocoa 的大部分部分都不是异常安全的。这不是这里的问题。我希望我现在说清楚了。

【问题讨论】:

    标签: objective-c cocoa exception exception-handling


    【解决方案1】:

    为什么我应该编写一个@catch(...) 块来处理程序员错误而不是修复实际错误?

    在大多数情况下,您不会。在 Objective-C 中,您通常不处理异常。如果发生异常,它会导致崩溃,然后您修复错误 - 希望您在测试期间发现它。

    当然,在某些情况下这行不通。也许你除了一个异常并且你可以解决它,所以你抓住它。或者有很少的 API 会抛出异常而不是使用错误对象。

    说实话,我在我的 Objective-C 代码中很少使用 try/catch。

    至于基本原理,我认为这主要是由于 Objective-C 的 C 传统。早在 80 年代初开发 Objective-C 时,异常是一种“新的”(即,还没有在许多主流语言中出现),并且 Objective-C 更迎合了使用 NULL 或 out 参数的 C 传统信号错误。

    【讨论】:

    • 我知道,这就是文档所说的。我想知道的是,如果你不应该处理异常,为什么 Cocoa 会使用它们。如果您不应该处理异常,为什么还要麻烦实际抛出异常呢? abort() 更加简单高效。
    • @Sven:在 Objective-C 中,这个想法是使用错误来表示可能出现的问题:网络连接失败、磁盘无法访问等等。异常是“异常的”,用于指示程序员应该自己检查的错误——例如数组越界异常。
    • 我明白这一点,您无需向我重复 Apple 的文档 - 我自己已经阅读过。问题是关于为什么
    • @Sven:我在回答中给出了“为什么”的理由。为了扩展,我认为添加了异常是因为预计一种语言应该有异常,但由于它的 C 传统,它们并没有被太多使用。异常最初甚至不是语言的一部分,而且 GCC 直到 v3.3 才支持 Objective-C 异常。即使是现在,您也必须使用标志启用 Objective-C 异常。
    • 补充:以前,异常纯粹是 Cocoa 的事​​情——你只能抛出一个 NSException 对象,你通过发送一个raise 消息来抛出它,并且 try 和 catch 被实现为在标题。
    【解决方案2】:

    您的问题明确假设“不应该抓住他们”。这是不正确的。在正常情况下,程序员不应该抓住它们,但这并不是说它们绝对不能出于任何目的而被抓住。

    示例:我不确定它是否会继续这样做,因为这些天它的错误要少得多,但我知道至少过去 Xcode 会捕获异常并弹出一个对话框说:“这样-和-发生了这种情况。这似乎不是一个严重的问题,但您应该保存并重新启动程序以避免将来出现任何问题。”

    【讨论】:

    【解决方案3】:

    为什么 Cocoa 会抛出异常 程序员错误(或断言)在 全部?一个人不应该抓住他们, 并实际编写处理 某处的程序员错误 无论如何调用堆栈都不是一个好主意

    啊!

    三个原因浮现在脑海。

    一,如果您在主运行循环中或多或少地捕获了异常,您可以将状态自动保存到临时位置,崩溃,并且在重新启动时会出现“尝试从崩溃前恢复,警告:可能会导致另一次崩溃你应该非常仔细地检查你的数据”对话框/表格/东西。或者甚至只是捕获异常并告诉用户执行“另存为”,退出并重新启动。

    第二,单元测试框架之类的东西很好地利用了异常来中止当前的测试(记录失败),并继续进行其余的测试。这可以让您查看您所做的更改是否有 一个 回归(恰好索引 NSArray 超出范围),或者您是否有 6 个回归(其中一个或多个抛出异常)。

    三,也许当它被添加到 ObjC 时,它旨在处理带有异常的多种错误,并且在现实世界的经验之后,有用的范围被确定为“仅几乎致命的错误”。

    【讨论】:

      【解决方案4】:

      避免抛出异常的主要原因是您可能会不小心将它们抛出到无法识别异常的堆栈帧中。例如,如果 table view 的数据源抛出异常,在委托方法将控制权返回给 table view 之前没有捕获和处理,它可能会导致各种麻烦,因为它会展开 table view 的堆栈帧,侧步各种临时对象和其他资源的发布。

      话虽如此,我个人喜欢异常并在我认为自然而然的地方使用它们,但需要注意的是永远不允许它们传播到未记录为异常感知的代码。

      【讨论】:

        【解决方案5】:

        可能有很多原因。其他人所涵盖的“历史原因”足以解释目前的情况,但还有其他可能性。

        另一种可能性是,Objective C 通常不是“资源获取即初始化”类型的语言(是的,这更像是库问题而不是语言问题,但它是真实的)。因此,大多数通过它抛出错误的 Objective C 代码都会留下无效的程序状态(事物仍然被锁定,超过保留的对象)。如果您考虑一下,您可以处理所有事情,但并非所有事情 RAII 都能神奇地修复(那里有很多异常不安全的 C++ 代码,而 C++ 主要是 RAII)。

        如上所述,您确实处理异常是免费的(ish),但实际上抛出一个异常是昂贵的(可能比额外参数和条件检查的成本高出一个或两个数量级)。因此,如果您的解析器(例如)使用它们来表示解析中的错误,那么如果您对错误参数进行显式检查,那么如果给定一个包含大量错误的文档可能需要更长的时间来解析。

        我个人喜欢异常,并且更愿意在“出错”时从我的库中抛出异常,但这不是 Cocoa 的方式,所以我使用异常来处理程序员错误和错误指示和 NSError** 用于其他事情。这不是很好,但它使其他人可以使用我的库,而无需学习编写 Objective C 代码的新方法。

        【讨论】:

          【解决方案6】:

          现代运行时给你零成本的异常,它给你的异常只有在抛出异常时才会产生成本。

          【讨论】:

          • 是的,我知道。 try/catch 块没有成本。抛出异常应该比以前更昂贵。但我并没有发明零成本例外这个名称,这就是 Apple 所说的这个功能。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2010-10-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-01-27
          相关资源
          最近更新 更多