2010-11-30 131 views
8

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

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

UPDATE:

link要求我们在应用级使用异常处理。有人曾经做过吗?请说明它的优点以及它可能产生的其他任何表现。

回答

30

简而言之:

不要使用异常来表示什么,但不可恢复的错误

只有适合使用@尝试/ @赶上来处理不可恢复的错误。使用@ throw/@ try/@ catch在iOS或Mac OS X上执行类似操作的控制流是绝对不合适的。即便如此,请仔细考虑是否最好使用异常来指示不可恢复的错误或仅仅是崩溃(调用abort());事故经常会留下更多的证据。

例如,除非您的目标是捕捉它们并以某种方式报告错误,否则 - 通常 - 崩溃或至少警告该错误,否则它不适合用于捕获越界异常用户表示您的应用处于不一致状态,并可能会丢失数据。

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


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

当然。

系统框架使用一种设计,其中任何异常被认为是致命的,不可恢复的错误;一个程序员错误,为所有意图和目的。这条规则的例外(heh)数量非常有限。

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

考虑这个调用堆栈:

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

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

如果your-code-2引发异常,异常通过system-code表示行为未定义; system-code可能会或可能不会让您的应用程序处于未定义的,潜在的崩溃或数据有损状态。

或者更为强烈:您不能在your-code-2中抛出异常,期望您可以在your-code-1中捕捉并处理它。

+0

你能解释一下:“通过系统框架代码抛出的任何异常的行为是未定义的。”详细? – Krishnan 2010-11-30 08:35:54

0

一般问自己,如果你试图表示错误,或者你实际上有特殊情况?如果是前者,那么不管任何性能问题,真实的还是感知的,这都是一个非常糟糕的主意。如果是后者,这绝对是正确的做法。

+2

不是真的; iOS中的例外情况适用于不可恢复的错误。 – bbum 2010-11-30 07:56:52

+1

对我来说,可恢复的情况是错误:) – jer 2010-11-30 14:27:54

6

我使用异常处理我相当密集的audio app没有任何问题。经过多次阅读和一些基准测试和反汇编分析后,我发现了一个有争议的结论:没有真正的理由不使用它们(智能地)并且没有足够的理由(NSError指针指针,无尽条件...呸!)。大多数人在论坛上发表的意见都是重复Apple Docs。

我去到相当多的细节,在this blog post,但我会在这里勾勒出我的发现:

误区1:@尝试/ @捕捉/ @终于过于昂贵(在CPU方面)

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

误区2:@try块具有成本在32位的iOS

苹果的文档“上零成本@try块”和国家的说出32位招致成本。小小的基准测试和反汇编分析似乎表明,在32位iOS(ARM处理器)上也有零成本的@try块。苹果的意思是说32bit 英特尔

误区3:这事项时抛出通过Cocoa框架例外的是未定义

是的,他们是“不确定”,但你在干什么通过苹果框架扔呢? 当然苹果不会为你处理它们。 实现可恢复错误异常处理的重点是在本地处理它们,而不是在本地“单线”处理。

这里的一个边缘案例是与像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; 
} 
// ... 

在这种情况下,除了将通过“通过Cocoa框架”(主线程的run loop)缺少你捕捉和崩溃。您可以使用GCD的dispatch_synch轻松解决此问题,并将方法调用加上其块参数加上任何异常处理。

为什么使用NSException的过谁的曾经像核心音频旧的基于C的框架之一所做的工作NSError的

任何人都知道什么是苦差事它是检查,处理和报告错误。 @ try/@ catch和NSExceptions提供的主要好处是让你的代码更简洁,更易于维护。

假设你有5行代码在文件上工作。每个可能会抛出一个,比如3个不同的错误(例如,磁盘空间不足,读取错误等)。不是将每行都包含在检查NO返回值的条件中,而是将NSError指针调查外包给另一个ObjC方法(或者更糟糕的是,使用的宏是#define!),则将所有5行全部包含在 @try中,并将其包装为 @try在那里处理每个错误。想想你会省下的路线!

通过创建NSException子类,您还可以轻松集中错误消息,并避免您的代码与他们散落在一起。您还可以轻松区分应用程序的“非致命”异常与致命程序员错误(如NSAssert)。您也可以避免使用“name”常量(子类的名称,即“名称”)。关于基准和拆卸的这一切

例子和更多的细节是on this blog post ...

例外加的try/catch /最后是(C++,Java,PHP和通过几乎使用所有其他主要语言范式, Ruby,Python)。也许是时候放弃这种偏执狂了,至少在iOS中也是如此。