在iPhone应用程序中使用NSException

我的一个朋友问我不要在iPhone应用程序中使用NSException。 他给的原因是“性能瓶颈”。 但我不相信。

有人可以确认我们应该禁止在iPhone应用程序中使用NSException吗? 如果您有使用NSException的最佳实践,请提供。

更新:

此链接要求我们在应用程序级别使用exception处理。 有人曾经做过吗? 请给它的好处和任何其他可能造成的performance。

简而言之:

不要使用exception来指示除了不可恢复的错误之外的任何内容

只能使用@ try / @ catch来处理不可恢复的错误。 在iOS或Mac OS X上使用@ throw / @ try / @ catch来执行控制stream操作是不合适的。即便如此,请仔细考虑是否最好使用exception来指示不可恢复的错误或者只是崩溃调用abort()); 事故往往会留下更多的证据。

例如,除非您的目标是捕捉它们并以某种方式报告错误,然后 – 通常 – 崩溃,或者至less警告用户您的应用程序,否则不适合用于捕获超出范围的exception处于不一致的状态,可能会丢失数据。

通过系统框架代码抛出的任何exception的行为是未定义的。


你能解释一下:“通过系统框架代码抛出的任何exception的行为是未定义的。” 详细?

当然。

系统框架使用任何exception被认为是致命的,不可恢复的错误的devise。 一个程序员的错误,所有的意图和目的。 这个规则的例外(heh)非常有限。

因此,在它们的实现中,如果通过系统框架代码抛出exception,那么系统框架将不能确保一切都必须被正确清理。 根据定义,正弦的例外是不可恢复的,为什么要支付清理费用?

考虑这个调用堆栈:

your-code-1() system-code() your-code-2() 

即代码将代码调用到调用更多代码的系统代码中(一种非常常见的模式,尽pipe调用堆栈明显更深)。

如果your-code-2抛出exception,exception通过system-code意味着行为是未定义的; system-code可能会或可能不会让您的应用程序处于一个未定义的,潜在的崩溃或数据有损的状态。

或者更强烈地说:你不能在your-code-2抛出一个exception,期望你可以在your-code-1捕获和处理它。

我用相当密集的audio应用程序exception处理,没有任何问题。 经过大量的阅读和一些基准testing和反汇编分析,我得出了一个有争议的结论:没有真正的理由不使用它们(智能地),并有足够的理由不要(NSError指针指针,无尽条件…呸!)。 大多数人在论坛上说的只是重复苹果文件。

我在这个博客文章中进入了相当多的细节,但是我会在这里概述我的发现:

神话1:@ try / @ catch / @终于太贵了(就CPU而言)

在我的iPhone 4上,抛出和捕捉100万个exception大约需要8.5秒。 这相当于每个只有大约8.5微秒。 您的实时CoreAudio线程昂贵吗? 也许有一点(但你永远不会抛出exception吗?),但UIAlert的8.5μs的延迟告诉用户有一个问题,打开他们的文件,是否会被注意到?

神话2:@try Blocks在32位iOS上花费不菲

苹果公司的文档中提到“ 64位的零成本@try块”,并声明32位产生成本。 一些基准testing和反汇编分析似乎表明,在32位iOS(ARM处理器)上也有零成本的@try块。 苹果是否说32bit的英特尔

神话3:重要的是通过cocoa框架抛出的例外是未定义的

是的,他们是“未定义的”,但是你究竟在通过苹果框架抛出什么呢? 当然,苹果公司不会为你处理它们。 实现可恢复错误的exception处理的重点是在本地处理它们,而不是在本地“单线”处理。

这里有一个边界情况是NSObject:performSelectorOnMainThread:waitUntilDone: 。 如果后面的参数是YES,这就像一个同步函数,在这种情况下,您可能会被原谅,期望exception能够到达您的调用范围。 例如:

 ///////////////////////////////////////////////////////////////////////// #pragma mark - l5CCThread ///////////////////////////////////////////////////////////////////////// @interface l5CCThread : NSThread @end @implementation l5CCThread - (void)main { @try { [self performSelectorOnMainThread:@selector(_throwsAnException) withObject:nil waitUntilDone:YES]; } @catch (NSException *e) { NSLog(@"Exception caught!"); } } - (void)_throwsAnException { @throw [NSException exceptionWithName:@"Exception" reason:@"" userInfo:nil]; } @end ///////////////////////////////////////////////////////////////////////// #pragma mark - l5CCAppDelegate ///////////////////////////////////////////////////////////////////////// @implementation l5CCAppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { l5CCThread *thd = [[l5CCThread alloc] init]; [thd start]; return YES; } // ... 

在这种情况下,exception将会通过“通过cocoa框架”(主线程的运行循环)来丢失你的catch和crash。 你可以使用GCD的dispatch_synch轻松地解决这个问题,并且把方法调用加上任何exception处理放在块参数中。

为什么使用NSException超过NSError的

任何曾经在Core Audio等基于C语言的早期框架中工作的人都知道,它正在检查,处理和报告错误。 @ try / @ catch和NSExceptions提供的主要好处是使代码更清洁,更易于维护。

假设你有5行代码在文件上工作。 每个可能会抛出一个,比如3个不同的错误(例如,磁盘空间不足,读取错误等)。 而不是包装每条线检查一个NO返回值,然后外部NSError指针调查到另一个ObjC方法(或更糟糕的是,使用#definemacros!),你把所有5行包装在一个 @try和处理每个错误都在那里。 想想你要保存的路线!

通过创buildNSException子类,您还可以轻松集中错误消息,并避免让代码乱丢。 您还可以轻松区分应用程序的“非致命”exception与致命的程序员错误(如NSAssert)。 您也可以避免使用“name”常量(子类名称,即“名称”)。

关于基准和反汇编的所有这些和更多细节的例子都在这个博客文章中 。

exception加try / catch / finally是几乎所有其他主要语言(C ++,Java,PHP,Ruby,Python)所使用的范例。 也许是时候放弃这个偏执狂了,至less在iOS里也是如此。

一般问自己,如果你试图表明一个错误,或者你真的有一个特殊的条件? 如果是前者,那么不pipe任何性能问题,这是一个非常糟糕的主意。 如果是后者,这绝对是正确的做法。