在iPhone应用程序中使用NSException

Kri*_*nan 9 iphone nsexception ios

我的一个朋友让我不要在iPhone应用程序中使用NSException.他给出的原因是"性能瓶颈".但我不相信它.

有人可以确认我们应该限制在iPhone App中使用NSException吗?如果您有使用NSException的最佳实践,请同时提供.

更新:

链接要求我们在应用级别使用异常处理.有人做过吗?请提供它的优点以及它可以创建的任何其他性能故障.

bbu*_*bum 31

简而言之:

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

只使用@ try/@ catch来处理不可恢复的错误.使用@ throw/@ try/@ catch在iOS或Mac OS X上执行类似操作的控制流是绝对不合适的.即使这样,也要仔细考虑是否最好使用异常来指示不可恢复的错误或只是崩溃( call abort()); 崩溃往往会留下更多的证据.

例如,用于捕获越界异常是不合适的,除非你的目标是捕获它们并以某种方式报告错误,然后 - 通常 - 崩溃,或者至少警告用户你的应用程序处于不一致状态,可能会丢失数据.

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


你能解释一下"通过系统框架代码抛出的任何异常的行为都是未定义的".详细地?

当然.

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

因此,在它们的实现中,如果抛出通过系统框架代码的异常,系统框架将不能确保必须正确地清理所有内容.根据定义,正例是不可恢复的,为什么要支付清理费用?

考虑这个调用堆栈:

your-code-1()
    system-code()
        your-code-2()
Run Code Online (Sandbox Code Playgroud)

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

如果your-code-2抛出异常,则异常传递system-code意味着行为未定义; system-code可能会也可能不会使您的应用程序处于未定义,可能崩溃或数据丢失的状态.

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


Har*_*ngh 6

我已经为我相当密集的音频应用程序使用异常处理而没有任何问题.经过大量的阅读和一些基准测试和反汇编分析,我得出了一个有争议的结论,即没有真正的理由不使用它们(智能地)和充足的理由(NSError指针指针,无限条件......)呸!).人们在论坛上说的大部分内容都只是重复Apple Docs.

我在这篇博文中详细介绍了一些细节,但我将在概述我的发现:

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

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

神话2:@try Blocks在32位iOS上有成本

Apple文档谈到" 64位零成本@try块"并指出32位会产生成本.一个小的基准测试和反汇编分析似乎表明在32位iOS(ARM处理器)上也存在零成本的@try块.苹果意味着说32位英特尔吗?

神话3:通过Cocoa框架抛出的异常很重要是未定义的

是的,它们是"未定义的",但是你还在做什么呢?当然, 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;
}
// ...
Run Code Online (Sandbox Code Playgroud)

在这种情况下,异常将通过"通过cocoa框架"(主线程的运行循环)传递,错过了捕获和崩溃.你可以使用GCD轻松解决这个问题dispatch_synch,并在方法调用和任何异常处理中加入它的块参数.

为什么在NSError上使用NSException

任何曾经在像Core Audio这样的旧的基于C的框架中工作的人都知道它正在检查,处理和报告错误.@ try/@ catch和NSExceptions提供的主要好处是使代码更清晰,更易于维护.

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

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

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

异常加上try/catch/finally是几乎所有其他主要语言(C++,Java,PHP,Ruby,Python)使用的范例.也许是时候放弃妄想并拥抱它......至少在iOS中如此.

  • 建议可以将NSExceptions用于可恢复的错误和流量控制是不正确的.框架显然不支持这一点. (7认同)
  • 因为您将每种错误类型的处理集中到给定代码块的单个位置而不是每行. (2认同)