我的一个朋友告诉我在iPhone应用程序中不要使用NSException。他给出的原因是“性能瓶颈”。但我并不信服。
有人可以证实我们应该避免在iPhone应用程序中使用NSException吗?如果您有使用NSException的最佳实践,请提供。
更新:
这个链接要求我们在应用程序级别使用异常处理。有人做过吗?请给出其优点以及可能带来的任何其他性能问题。
我的一个朋友告诉我在iPhone应用程序中不要使用NSException。他给出的原因是“性能瓶颈”。但我并不信服。
有人可以证实我们应该避免在iPhone应用程序中使用NSException吗?如果您有使用NSException的最佳实践,请提供。
更新:
这个链接要求我们在应用程序级别使用异常处理。有人做过吗?请给出其优点以及可能带来的任何其他性能问题。
简言之:
只有在处理不可恢复的错误时使用@try/@catch才是适当的做法。在iOS或Mac OS X上,从未使用@throw/@try/@catch进行流程控制都是合适的。即使如此,在指示不可恢复的错误还是仅仅崩溃(调用abort())中仔细考虑一下; 崩溃通常会留下更多证据。
例如,不能使用捕获越界异常的方法,除非您的目标是捕获它们并以某种方式报告错误,然后 - 通常 - 崩溃或者至少警告用户你的应用处于不一致状态且可能会丢失数据。
通过系统框架代码抛出的异常的行为是未定义的。
你能详细解释一下“通过系统框架代码抛出的异常的行为是未定义的”吗?
可以。
系统框架采用这样的设计:任何异常都被视为致命的、不可恢复的错误; 就实际意义而言,是一个程序员错误。除此之外,这个规则有非常有限的例外。
因此,在它们的实现中,如果抛出通过系统框架代码传递的异常,系统框架不会确保所有东西都被适当地清理。既然异常是不可恢复的,为什么要支付清理成本呢?
考虑这个调用堆栈:
your-code-1()
system-code()
your-code-2()
即,您的代码调用系统代码,系统代码再调用您的更多代码(这是一种非常常见的模式,尽管调用堆栈显然更深)。
如果your-code-2
抛出异常,则异常传递到system-code
意味着行为未定义;system-code
可能会或可能不会使您的应用程序处于未定义的、可能崩溃或丢失数据的状态。
或者更强烈地说:您无法在your-code-2
中抛出异常,并期望在your-code-1
中捕获和处理它。
苹果文档提到“64位上的零成本@try块”,并指出32位会产生成本。一些基准测试和反汇编分析似乎表明,在32位iOS(ARM处理器)上也有零成本的@try块。苹果是不是想说32位的Intel呢?
是的,它们是“未定义的”,但是你为什么要通过Apple框架抛出它们呢?当然,苹果不会替你处理它们。实现可恢复错误的异常处理的整个重点是在本地处理它们,而不是每一行都在“本地”处理。
这里的一个边缘情况是像NSObject:performSelectorOnMainThread:waitUntilDone:
这样的方法。如果后面的参数是YES,这就像一个同步函数,这种情况下你可能会期望异常冒泡到你的调用范围。例如:
/////////////////////////////////////////////////////////////////////////
#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;
}
// ...
通常要问自己的是,你是在试图发出错误信号,还是实际上存在异常情况?如果是前者,那么无论任何性能问题,真实或感知到的,都是非常糟糕的想法。如果是后者,那么这绝对是正确的做法。