Objective-C 异常

发布于 2024-10-11 17:20:25 字数 518 浏览 6 评论 0原文

我刚刚完成 iPhone 应用程序编程课程。作为课程的一部分,我看到

  • Objective-C 使用 @try 指令提供异常处理
  • 系统库不使用异常处理,更喜欢 return nil

我问我是否应该对我编写的新代码使用异常处理(例如,如果我同时编写前端和逻辑代码,那么它们之间的通信就在我手中),但我被告知不,不应该对新代码使用异常。 (但他没有详细说明,然后班级继续前进,我想也许稍后原因会变得清楚。)

异常肯定优于 return nil 吗?您可以捕获特定类型,您不会试图通过忽略通常成功的函数的返回类型来忽略它们,您有可以记录的文本消息,它们允许您的代码专注于正常情况,从而更具可读性。 为什么使用异常

那么你觉得怎么样?我的培训师不使用 Objective-C 异常是正确的吗?如果是这样,为什么?

I have just completed an iPhone app programming course. As part of the course, I saw

  • Objective-C provides exception handling using the @try directive
  • The system library does not use exception handling, preferring to return nil

I asked if I should use exception handling for new code I wrote (e.g. if I write both the front-end and logic code, so the communication between them is in my hands) but I was told no, one should not use exceptions for new code. (But he failed to elaborate, then the class moved on, I thought perhaps the reason would become clear later..)

Surely exceptions are superior to return nil? You can catch particular types, you are not tempted to ignore them by ignoring the return type of a function which normally succeeds, you have text messages which can be logged, they allow your code to focus on the normal case and thus be more readable. Why to use exceptions.

So what do you think? Was my trainer right not to use Objective-C exceptions? If so, why?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

海拔太高太耀眼 2024-10-18 17:20:25

在资源不自动管理的情况下抛出异常是不安全的。 Cocoa 框架(和邻居框架)就是这种情况,因为它们使用手动引用计数。

如果抛出异常,则通过展开堆栈跳过的任何 release 调用都将导致泄漏。仅当您确定不会恢复时,这才会限制您抛出异常,因为当进程退出时,所有资源都会返回到操作系统。

不幸的是,NSRunLoop 倾向于捕获传播给它们的所有异常,因此如果您在事件期间抛出异常,您将继续处理下一个事件。显然,这非常糟糕。因此,最好不要扔。

如果您使用垃圾收集的 Objective-C,这个问题就会减少,因为 Objective-C 对象表示的任何资源都将被正确释放。但是,未包装在 Objective-C 对象中的 C 资源(例如文件描述符或 malloc 分配的内存)仍然会泄漏。

所以,总而言之,不要扔。

正如您所提到的,Cocoa API 有几种解决方法。返回 nilNSError** 模式是其中两个。

ARC 说明

ARC 用户可以选择启用或禁用完全异常安全。启用异常安全后,ARC 将生成代码以在其范围被终止时释放强引用,从而可以安全地在代码中使用异常。 ARC 不会修补外部库以在其中启用异常支持,因此即使在程序中启用了异常支持,您也应该小心抛出的位置(尤其是捕获的位置)。

可以使用 -fobjc-arc-exceptions 启用或使用 -fno-objc-arc-exceptions 禁用 ARC 异常支持。默认情况下,它在 Objective-C 中处于禁用状态,但在 Objective-C++ 中处于启用状态。

Objective-C 中的完全异常安全默认情况下处于禁用状态,因为 Clang 作者假设 Objective-C 程序无论如何都不会从异常中恢复,并且因为与该清理相关的代码大小成本较大且性能损失较小。另一方面,在 Objective-C++ 中,C++ 已经引入了大量的清理代码,人们更有可能真正需要异常安全。

这全部来自 LLVM 网站上的 ARC 规范

It is unsafe to throw exceptions in circumstances where resources are not automatically managed. This is the case of the Cocoa framework (and neighbor frameworks), as they use manual reference counting.

If you throw an exception, any release call you skip over by unwinding the stack will result in a leak. This should limit you tothrowing only if you're certain that you're not going to recover since all resources are returned to the OS when a process quits.

Unfortunately, NSRunLoops tend to catch all exceptions that propagate to them, so if you throw during an event, you'll resume to the next event. This is, obviously, very bad. Therefore, it's better that you simply don't throw.

This problem is diminished if you use garbage-collected Objective-C, as any resource represented by an Objective-C object will be properly released. However, C resources (such as file descriptors or malloc-allocated memory) that are not wrapped in an Objective-C object will still leak.

So, all in all, don't throw.

The Cocoa API has several workarounds to this, as you mentioned. Returning nil and the NSError** pattern are two of them.

Clarifications for ARC

ARC users can choose to enable or disable full exception safety. When exception safety is enabled, ARC will generate code to release strong references when their scope is killed, making it safe to use exception in your code. ARC will not patch external libraries to enable exception support in them, so you should be careful where you throw (and especially where you catch), even with exception support enabled in your program.

ARC exception support can be enabled with -fobjc-arc-exceptions or disabled with -fno-objc-arc-exceptions. By default, it is disabled in Objective-C but enabled in Objective-C++.

Full exception safety in Objective-C is disabled by default because the Clang authors assume that Objective-C programs will not recover from an exception anyways, and because there is a large code size cost and a small performance penalty associated to that cleanup. In Objective-C++, on the other hand, C++ already introduces a lot of cleanup code, and people are much more likely to actually need exception-safety.

This is all from the ARC specification on the LLVM website.

星星的軌跡 2024-10-18 17:20:25

在 Cocoa 和 iOS 编程中,异常用于指示不可恢复的程序员错误。当框架抛出异常时,表明框架检测到不可恢复且内部状态现在未定义的错误状态。

此外,通过框架代码抛出的异常会使框架处于未定义状态。也就是说,你不能做类似的事情:

void a() {
    @throw [MyException exceptionWithName: @"OhNoes!"  ....];
}

@try {
    ... call into framework code that calls a() above ...
} @catch (MyException e) {
    ... handle e ...
}

底线:

异常不能在 Cocoa 中用于流程控制、用户输入验证、数据有效性检测或以其他方式指示可恢复的错误。为此,您可以使用 NSError** 模式作为 记录在此处(感谢 Abizem)。

(请注意,有少数 API 确实违反了这一点——其中使用异常来指示可恢复状态。已针对这些 API 提交了错误以弃用并最终消除这些不一致之处。)


最后 找到了我正在寻找的文档

重要提示:您应该保留使用权
编程的例外情况或
意外的运行时错误,例如
越界集合访问,
尝试改变不可变的对象,
发送无效消息并丢失
与窗口服务器的连接。
你通常会处理这些事情
错误与例外时
正在创建应用程序
比运行时。

如果您有现有的代码体
(例如第三方库)
使用异常来处理错误
有条件的话,您可以按原样使用代码
在您的 Cocoa 应用程序中。但你
应确保任何预期的
运行时异常不会从
这些子系统最终会出现在
呼叫者的代码。例如,一个解析
库可能会使用异常
内部指出问题并
启用解析快速退出
可能是深度递归的状态;
但是,你应该小心抓住
顶层的此类例外
图书馆并将它们翻译成
适当的返回代码或状态。

In Cocoa and iOS programming, exceptions are used to indicate non-recoverable programmer error. When an exception is thrown by the frameworks, it indicates that the frameworks have detected an error state that is both not recoverable and for which the internal state is now undefined.

As well, exceptions thrown through framework code leave the frameworks in an undefined state. I.e. you can't do something like:

void a() {
    @throw [MyException exceptionWithName: @"OhNoes!"  ....];
}

@try {
    ... call into framework code that calls a() above ...
} @catch (MyException e) {
    ... handle e ...
}

Bottom line:

Exceptions are not to be used in Cocoa for flow control, user input validation, data validity detection or to otherwise indicate recoverable errors. For that, you use the NSError** pattern as documented here (thanks Abizem).

(Note that there is a small number of API that does violate this -- where an exception is used to indicate a recoverable state. Bugs have been filed against these to deprecate and eventually remove these inconsistencies.)


Finally found the document I was looking for:

Important: You should reserve the use
of exceptions for programming or
unexpected runtime errors such as
out-of-bounds collection access,
attempts to mutate immutable objects,
sending an invalid message, and losing
the connection to the window server.
You usually take care of these sorts
of errors with exceptions when an
application is being created rather
than at runtime.

If you have an existing body of code
(such as third-party library) that
uses exceptions to handle error
conditions, you may use the code as-is
in your Cocoa application. But you
should ensure that any expected
runtime exceptions do not escape from
these subsystems and end up in the
caller’s code. For example, a parsing
library might use exceptions
internally to indicate problems and
enable a quick exit from a parsing
state that could be deeply recursive;
however, you should take care to catch
such exceptions at the top level of
the library and translate them into an
appropriate return code or state.

香橙ぽ 2024-10-18 17:20:25

我认为,如果我错了,其他人会纠正我,异常应该用于捕获程序员错误,而 NSError 类型错误处理应该用于程序运行时发生的异常情况。

至于返回 nil,这还不是全部 - 可能有问题的函数不仅仅返回 nil,它们还可以(并且应该)使用以下方式提供更多信息:作为参数传入的 NSError 对象。

另请参阅

I think, and others will correct me if I am wrong, that Exceptions should be used to catch programmer errors, while the NSError type error handling should be used for exceptional conditions that occur while the program is running.

And as for returning nil, that isn't all - functions that might have problems don't just return a nil, they also can (and should) provide further information using the NSError object that is passed in as a parameter.

See also

旧街凉风 2024-10-18 17:20:25

就我个人而言,我认为没有理由不在在您自己的代码中使用异常。异常模式对于处理错误比

  • 总是有返回值更干净,
  • 确保可能的返回值之一确实意味着“错误”,
  • 传递一个额外的参数,该参数是对 NSError* 的引用,
  • 该参数洒在您的为每个错误返回清理代码的代码,或者具有明确的 goto 跳转到公共错误清理部分的代码。

但是,您需要记住,其他人的代码不能保证正确处理异常(如果是纯 C,实际上它无法正确处理异常)。因此,您决不能允许异常传播到代码边界之外。例如,如果您在委托方法的内部深处抛出异常,您必须在返回委托消息的发送者之前处理该异常

Personally, I see no reason not to use exceptions in your own code. The exception pattern is cleaner for handling errors than

  • always having a return value,
  • making sure that one of the possible return values really means "error"
  • passing an extra parameter which is a reference to an NSError*
  • sprinkling your code with clean up code for every error return or having explicit gotos to jump to a common error cleanup section.

However, you need to bear in mind that other people's code is not guaranteed to handle exception properly (if it's pure C, in fact it can't handle exceptions properly). Thus, you must never ever allow an exception to propagate beyond the boundaries of your code. For instance, if you throw an exception deep in the bowels of a delegate method, you must handle that exception before returning to the sender of the delegate message.

暗藏城府 2024-10-18 17:20:25

使用的错误处理类型取决于您想要完成的任务。几乎所有 Apple 的方法都会填充一个 NSError 对象,可以在出现错误时访问该对象。

这种类型的错误处理可以与一段代码中的 try-catch 块结合使用:

-(NSDictionary *)getDictionaryWithID:(NSInteger)dictionaryID error:(NSError **)error
{
    @try
    {
         //attempt to retrieve the dictionary
    }
    @catch (NSException *e)
    {
        //something went wrong
        //populate the NSError object so it can be accessed
    }
}

简而言之,该方法的用途决定了您应该使用的错误处理类型。但是,在上面的示例中,您可以同时使用两者。

try-catch 块的常见用途:

@try
{
    [dictionary setObject:aNullObject forKey:@"Key"];
}
@catch (NSException *e)
{
    //one of the objects/keys was NULL
    //this could indicate that there was a problem with the data source
}

希望这会有所帮助。

The type of error handling used depends on what you are trying to accomplish. Almost all of Apple's methods will populate a NSError object that can be accessed upon error.

This type of error handling can be used in conjunction with a try-catch block within a section of code:

-(NSDictionary *)getDictionaryWithID:(NSInteger)dictionaryID error:(NSError **)error
{
    @try
    {
         //attempt to retrieve the dictionary
    }
    @catch (NSException *e)
    {
        //something went wrong
        //populate the NSError object so it can be accessed
    }
}

In a nutshell, the purpose of the method determines the type of error handling you should use. But, in this example above, you can use both.

A common use for the try-catch block:

@try
{
    [dictionary setObject:aNullObject forKey:@"Key"];
}
@catch (NSException *e)
{
    //one of the objects/keys was NULL
    //this could indicate that there was a problem with the data source
}

Hope this helps.

韬韬不绝 2024-10-18 17:20:25

我认为你的教练是正确的。您可以提出各种支持或反对异常的论据,但最重要的是,如果您希望经验丰富的 Cocoa 开发人员能够正确地“闻到”您的代码,那么您将使用 Apple 在其代码中使用的相同设计模式来实现您的应用程序,并且在他们的框架中。这意味着 nil 和 NSError 而不是异常。

因此,不使用异常的优点主要在于一致性和熟悉性。

另一件需要考虑的事情是 Objective C 中的 nil 通常是相当“安全的”。也就是说,您可以向 nil 发送任何消息,并且您的应用程序不会崩溃。

(我还应该指出,Apple 确实在某些地方使用异常,通常是在您错误地使用 API 的地方。)

I think your trainer is correct. You can make all kinds of arguments for and against exceptions but the bottom line is if you want your code to "smell" right to an experienced Cocoa developer, you'll implement your application using the same design patterns that Apple use in their code and in their frameworks. That means nils and NSErrors rather than exceptions.

The advantages of not using exceptions are therefore mainly around consistency and familiarity.

An other thing to consider is that a nil in Objective C is usually fairly "safe." That is, you can send any message to a nil and your application won't crash.

(I should also point out that Apple do use exceptions in some places, usually where you're using an API incorrectly.)

梦幻之岛 2024-10-18 17:20:25

如果您喜欢例外,那么您可以使用它们。如果您这样做,我建议您谨慎使用它们,因为它变得很难维护(您通常必须在运行时执行额外的退出点以证明程序的正确性)。

没有针对客户的异常处理期望的规范;他们必须根据文档维护他们的程序(乏味,容易出错,在抛出异常之前可能不会维护)。

如果我要使用它们,我只会在极少数情况下使用它们,并在可能的情况下使用错误代码或返回 nil 。另外,我会在库内部使用它们,而不会让客户端暴露在接口中的异常(或副作用)中。我不在 objc 或 c++ 中使用它们(好吧,我会抓住它们,但不会扔掉它们)。

正如您通常所期望的那样,您应该避免使用它们来控制程序流(常见的误用)。

当您必须逃避某种崩溃时,最好使用它们。如果您现在要继续编写代码,我建议您只编写接口来处理错误作为程序流程的一部分,并尽可能避免编写异常。

对原始帖子的更正:cocoa libs 更喜欢返回 nil,在某些情况下它们会为您抛出异常(捕获)。

if you prefer exceptions, then you can use them. if you do, i recommend you use them sparingly because it becomes very hard to maintain (additional exit points which you'll typically have to exercise at runtime to prove the program's correctness).

there's not a specification for exception handling expectations for clients; they have to maintain their program based on the docs (tedious, error prone, probably won't be maintained until an exception is thrown).

if i were to use them, i'd use them only in very rare cases and use error codes or return nil where possible. plus, i'd use them internal to a library and not expose the clients to exceptions in the interface (or the side effects). i don't use them in objc or c++ (well, i will catch them but i won't throw them).

as you'd usually expect, you should avoid using them to control program flow (a common misuse).

it's better to use them when you have to escape a certain crash. if you're going forward and writing the code now, i recommend you just write the interface to handle the errors as part of the program flow and avoid writing exceptions where possible.

correction to original post: cocoa libs prefer to return nil, in some cases they will throw exceptions for you (to catch).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文