8

我正在将 Java 代码库移植到 Cocoa/Objective-C 以在桌面 Mac OS X 上使用。Java 代码有很多带有检查异常的方法,例如:

double asNumber() throws FooException {
    ...
}

在 Objective-C 中表示这些的最佳方式是什么?异常或错误输出参数?

- (CGFloat)asNumber { 
    ... // possibly [FooException raise:format:];
}

或者

- (CGFloat)asNumberError:(NSError **)outError {
    ...
}

我的感觉是,对于 Objective-C 来说,错误通常是更好的解决方案,但是正如你所看到的......很多像上面这样的方法看起来会很尴尬。再说一次,有很多这样的。

当然请记住,由于这些是Java 中的已检查异常,因此我需要在调用这些方法的任何地方添加@try块或检查(很多地方)。if (*outError) {...}

我记得听说@try在 Objective-C 中输入块曾经很昂贵,但在 64 位或 SL 或其他一些新环境中它很便宜(不记得确切)。我完全不关心向后兼容性,所以我绝对愿意只为新的热点而设计。

4

6 回答 6

16

绝对应该避免从字符串中解析数字之类的异常。在 Objective-C 中,异常代表程序员错误,而不是用户输入错误,甚至是不可用的文件。(部分原因是异常处理总是比更“常规”的错误处理更昂贵和更复杂。不管输入@try块在 64 位中是“零成本”的事实,但无论何时实际引发异常,它仍然很慢。 ) 当然,您可以随意使用异常,但这不是 Cocoa 的方式,您会发现自己与其他 Objective-C 代码不一致。使用您的代码的人会非常恼火,因为您在本应导致错误的情况下抛出异常。

来自Apple 自己的文档

“在许多环境中,异常的使用相当普遍。例如,您可能会抛出异常来表示例程无法正常执行,例如文件丢失或数据无法正确解析时。异常是资源密集型的在 Objective-C 中。您不应该将异常用于一般的流程控制,或者仅仅表示错误。相反,您应该使用方法或函数的返回值来指示发生了错误,并在错误对象。”

看看内置的 Cocoa 类如何处理这样的错误。例如,NSString有类似-floatValue的方法,如果失败则返回 0。对于您的特定情况,一个更好的解决方案可能是NSScanner是如何做到的,例如在-scanFloat:- 接受指向应该存储结果的字段的指针,并根据解析是否成功返回YES或返回。NO

除了 Obejctive-C 约定和最佳实践之外,NSError 比 NSException 更加健壮和灵活,并且允许调用者在需要时有效地忽略问题。我建议通读Cocoa 的错误处理编程指南注意:如果你接受一个NSError**参数,我强烈建议你也设计允许客户端在NULL他们不想接收任何错误信息时通过。我知道的每个 Cocoa 类都会为错误执行此操作,包括 NSString。

尽管移植的代码最终可能看起来与 Java 代码完全不同,但要认识到它将由 Objective-C 代码使用,而不是 Java 等效的相同客户端。绝对符合语言的成语。移植不会是 Java 代码的镜像,但结果会更正确(对于 Objective-C)。

于 2009-07-13T01:36:40.063 回答
7

在 Cocoa 中,异常实际上只应该用于“编程错误”;其理念是让应用程序抓住他们,让用户选择保存他们正在做的事情,然后退出。一方面,并​​非所有框架或代码路径都是 100% 异常安全的,因此这可能是唯一安全的做法。对于可以预期和从中恢复的错误,您应该使用 NSError,通常通过 out 参数。

于 2009-07-13T01:39:35.240 回答
3

您说得对,“输出错误通常是 ObjC 的更好解决方案”。您很少会在 Cocoa 中找到引发异常的 API(除非您不满足 API 的先决条件,但在这种情况下,默认情况下行为是未定义的)。

如果您希望此代码能够超越您并被其他 Cocoa 开发人员采用,我建议您使用 out errors。我编写的代码是由不熟悉 Cocoa 并且大量使用异常的人构建的,而要解决它们是一件非常痛苦的事情。

于 2009-07-13T01:33:03.347 回答
2

我是 Objective-C 使用的 out error 方法的忠实粉丝。你必须处理异常,但如果你愿意,你可以选择忽略错误。这一切都符合 Objective-C 的“程序员知道他们在做什么”的态度。它还使 Objective-C 成为一种看起来非常干净的语言,因为您的代码不会被 try-catch 块弄乱。

也就是说 - 您可能需要考虑:是否存在忽略异常的情况?你抛出的异常真的很重要吗?您是否发现自己正在编写简单的 catch 块来清理变量并继续?我倾向于排除错误,因为我喜欢语法,并且 Objective-C 只为最严重的错误保留异常。

于 2009-07-13T01:42:45.720 回答
2

看起来这些检查的异常更清楚地映射到错误。仍然可以使用例外,但应保留用于特殊情况。

于 2009-07-13T01:51:23.057 回答
-1

异常可能是最好的方法,因为 64 位 Obj-C ABI(运行时)使用零成本异常,因此您可以免费获得更简洁的代码。当然,在 32 位中,旧的 setjmp/longjmp 异常仍在使用中,它们不与 C++ 交互,所以如果这是一个目标,那么你就有问题了。

于 2009-07-13T01:25:51.763 回答