Objective-Cexception
我刚刚完成了一个iPhone应用程序编程课程。 作为课程的一部分,我看到了
- Objective-C使用
@try
指令提供exception处理 - 系统库不使用exception处理,宁愿
return nil
我问我是否应该为我写的新代码使用exception处理(例如,如果我写了前端和逻辑代码,所以它们之间的通信在我手中),但我被告知不,不应该使用exception码。 (但是他没有详细说明,那么class上的人就走了,我想也许以后会明白的。)
当然,例外优于return nil
? 你可以捕获特定的types,你不会忽略通常会成功的函数的返回types,你可以logging文本消息,它们允许你的代码专注于正常情况,从而更具可读性。 为什么要使用例外 。
所以你怎么看? 我的培训师是否有权利不使用Objective-Cexception? 如果是这样,为什么?
在不自动pipe理资源的情况下抛出exception是不安全的。 这是Cocoa框架(和邻居框架)的情况,因为他们使用手动引用计数。
如果抛出一个exception,任何release
调用通过展开堆栈跳过将导致泄漏。 这应该限制你只有当你确定你不打算恢复,因为所有的资源都返回到操作系统,当一个进程退出。
不幸的是, NSRunLoop
倾向于捕获传播给它们的所有exception,所以如果你在事件中抛出exception,你将恢复到下一个事件。 这显然非常糟糕。 所以,最好不要扔。
如果使用垃圾收集的Objective-C,这个问题会减less,因为任何由Objective-C对象表示的资源都将被正确释放。 但是,不包含在Objective-C对象中的C资源(如文件描述符或malloc
分配的内存)仍然会泄漏。
所以,总而言之,不要抛弃。
正如你所提到的,Cocoa API有几个解决方法。 返回nil
和NSError**
模式是其中两个。
ARC的说明
ARC用户可以select启用或禁用完整的exception安全。 当启用exception安全时,ARC将生成代码,以便在其作用域被终止时释放强引用,从而安全地在代码中使用exception。 ARC不会修补外部库来启用对它们的exception支持,因此即使在程序中启用了exception支持,您也应该小心谨慎(尤其是在捕获的地方)。
ARCexception支持可以通过-fobjc-arc-exceptions
来启用,或者通过-fno-objc-arc-exceptions
禁用。 默认情况下,它在Objective-C中被禁用,但在Objective-C ++中被启用。
Objective-C中的完全exception安全性在默认情况下是禁用的,因为Clang作者认为Objective-C程序无论如何不会从exception中恢复,并且由于与清理相关的代码量很大,性能损失很小。 另一方面,在Objective-C ++中,C ++已经引入了大量的清理代码,人们更有可能需要exception安全。
这些都来自LLVM网站上的ARC规范 。
在Cocoa和iOS程序员中,exception用于指示不可恢复的程序员错误。 当框架引发exception时,表明框架已经检测到一个错误状态,这个错误状态是不可恢复的,现在内部状态是未定义的。
而且,通过框架代码抛出的exception使框架处于未定义的状态。 也就是说你不能做这样的事情:
void a() { @throw [MyException exceptionWithName: @"OhNoes!" ....]; } @try { ... call into framework code that calls a() above ... } @catch (MyException e) { ... handle e ... }
底线:
Cocoa中不会使用exception进行stream量控制,用户inputvalidation,数据有效性检测或者以其他方式指示可恢复错误。 为此,您使用NSError**
模式在此处logging (感谢Abizem)。
(请注意,有一小部分API违反了这个规定 – 在这种情况下,使用exception来指示可恢复的状态。已经对这些错误提出了错误,并最终消除这些不一致。)
最后find我正在寻找的文件 :
重要提示:您应该保留使用exception编程或意外的运行时错误,如超出范围的集合访问,试图改变不可变的对象,发送无效的消息,并失去与窗口服务器的连接。 在创build应用程序时,而不是在运行时,您通常会处理这些types的exception。
如果您拥有使用exception处理错误情况的现有代码体(如第三方库),则可以在Cocoa应用程序中按原样使用代码。 但是您应该确保任何预期的运行时exception不会从这些子系统中转义,并最终以调用者的代码结束。 例如,parsing库可能会在内部使用exception来指示问题,并且可以从可能深度recursion的parsing状态快速退出; 但是,您应该注意在库的顶层捕捉这些exception,并将其转换为适当的返回代码或状态。
我认为,如果我错了,其他人会纠正我,应该用exception来捕捉程序员错误,而NSError
typeserror handling应该用于程序运行时发生的exception情况。
至于返回nil
,这不是全部 – 可能有问题的函数不只是返回一个nil
,他们还可以(也应该)提供进一步的信息使用作为参数传入的NSError对象。
也可以看看
- error handling编程指南
- 一个有用的博客post来自cocoa是我的女朋友。
就我个人而言,我没有理由不在你自己的代码中使用exception。 exception模式比处理错误更清晰
- 总是有一个返回值,
- 确保其中一个可能的返回值真的意味着“错误”
- 传递一个额外的参数,这是一个参考
NSError*
- 用每个错误返回的清理代码来散布代码,或者显式地跳转到常见的错误清理部分。
但是,需要注意的是,其他人的代码不能保证能正确处理exception(如果是纯C的,实际上不能正确处理exception)。 因此,你永远不要让exception传播到你的代码的边界之外。 例如,如果您在委托方法的深处抛出exception,则必须在返回委托消息的发件人之前处理该exception 。
所使用的error handlingtypes取决于您要完成的操作。 几乎所有的苹果方法都会填充一个错误的NSError
对象。
这种types的error handling可以与一段代码中的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 } }
简而言之,该方法的目的决定了您应该使用的error handlingtypes。 但是,在上面的这个例子中,你可以使用两者。
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 }
希望这可以帮助。
我认为你的教练是正确的。 您可以针对exception做出各种各样的论据,但底线是如果您希望自己的代码对于有经验的Cocoa开发人员来说是“正确的”,那么您将使用Apple在代码中使用的相同devise模式来实现您的应用程序,在他们的框架。 这意味着nil
和NSError
而不是例外。
不使用例外的好处主要围绕一致性和熟悉性。
另外要考虑的是Objective C中的nil
通常是相当“安全的”。 也就是说,你可以发送任何消息到nil
,你的应用程序不会崩溃。
(我也应该指出,苹果在一些地方使用exception,通常是错误地使用API的地方)。
如果你喜欢例外,那么你可以使用它们。 如果你这样做,我build议你谨慎使用它,因为它变得非常难以维护(在运行时通常需要额外的出口点来certificate程序的正确性)。
对于客户的exception处理期望没有规范。 他们必须根据文档维护他们的程序(单调乏味,容易出错,在抛出exception之前可能不会被维护)。
如果我要使用它们,我只会在非常罕见的情况下使用它们,并使用错误代码或在可能的情况下返回零。 再加上,我会使用它们内部的库,而不是暴露在接口(或副作用)的exception。 我不使用它们在objc或c + +(嗯,我会抓住他们,但我不会扔他们)。
正如您通常所期望的那样,您应该避免使用它们来控制程序stream程(常见的误用)。
当你必须逃脱某个特定的崩溃时,最好使用它们。 如果您现在正在编写代码,那么我build议您只编写接口来处理错误,作为程序stream的一部分,并尽可能避免编写exception。
更正原来的post:cocoa库更喜欢返回零,在某些情况下,他们会抛出exception(抓)。