在objective-c / cocoa中抛出一个exception

在objective-c / cocoa中抛出exception的最好方法是什么?

我使用[NSException raise:format:]如下:

 [NSException raise:@"Invalid foo value" format:@"foo of %d is invalid", foo]; 

这里谨慎的一句话。 在Objective-C中,与许多类似的语言不同,您通常应该尽量避免在正常操作中可能出现的常见错误情况中使用exception。

针对Obj-C 2.0的Apple文档声明如下:“重要:exception在Objective-C中占用大量资源,不应该将exception用于一般的stream程控制,或者只是为了表示错误(如文件不可访问)”

苹果公司的概念性exception处理文档解释相同,但用更多的话来说:“重要:你应该保留使用exception编程或意外的运行时错误,如超出范围集合访问,试图改变不可变的对象,发送一个无效的消息,并且失去与窗口服务器的连接,当你创build一个应用程序而不是在运行时,你通常会处理这些types的exception[…..]而不是exception,错误对象(NSError)和Cocoa错误传递机制是在Cocoa应用程序中沟通预期错误的推荐方式。“

其原因部分是遵循Objective-C中的编程习惯用法(在更简单的情况下使用返回值,在更复杂的情况下使用通过引用参数(通常是NSError类)),部分原因是抛出exception和捕获exception更为昂贵,最后(最重要的是)Objective-Cexception是C的setjmp()和longjmp()函数的一个简单的包装,基本上搞乱了你仔细的内存处理,请看这个解释 。

 @throw([NSException exceptionWith…]) 

我没有代表评论eJames的回应,所以我想我需要把我的这里。 对于那些来自Java背景的人,您会记得Java区分了Exception和RuntimeException。 exception是一个检查的exception,并且RuntimeException未被选中。 特别是,Javabuild议对“正常错误条件”使用已检查的exception,并对“由程序员错误导致的运行时错误”进行未检查的exception。 看起来Objective-Cexception应该用在你使用未经检查的exception的地方,在你使用检查exception的地方,首选错误代码返回值或NSError值。

我认为是一致的更好使用@throw与自己的类扩展NSException。 然后你最后使用相同的符号来进行try catch:

 @try { ..... } @catch{ ... } @finally{ ... } 

苹果在这里解释如何抛出和处理exception: 捕捉exception 抛出exception

由于ObjC 2.0,Objective-Cexception不再是C的setjmp()longjmp()的封装,并且与C ++exception兼容,@try是“免费的”,但是抛出和捕获exception的代价更高。

无论如何,断言(使用NSAssert和NSCAssertmacros家族)抛出NSException,并理智地使用它们作为里斯状态。

您可以使用两种方法在try catch块中引发exception

 @throw[NSException exceptionWithName]; 

或第二种方法

 NSException e; [e raise]; 

使用NSError来传达故障而不是exception。

关于NSError的快速点:

  • NSError允许C风格错误代码(整数)清楚地标识根本原因,并希望允许error handling程序来克服错误。 你可以非常容易地在NSError实例中包装像SQLite这样的C库的错误代码。

  • NSError也有作为一个对象的好处,并提供了一种方法来更详细地描述它的userInfo字典成员的错误。

  • 但是最重​​要的是,NSError不能被抛出,所以它鼓励更主动的error handling方法,与其他语言相比,其他语言只是简单地将热土豆扔进进一步的调用堆栈,在这一点上它只能被报告给用户没有以任何有意义的方式处理(如果你相信遵循OOP最大的信息隐藏原则)。

参考链接: 参考

这是我从“大书呆子牧场指南(第四版)”中学到的:

 @throw [NSException exceptionWithName:@"Something is not right exception" reason:@"Can't perform this operation because of this or that" userInfo:nil]; 

我相信你不应该使用exception来控制正常的程序stream。 但是,只要某个值与期望值不匹配,就应该抛出exception。

例如,如果某个函数接受一个值,并且该值永远不会被允许为零,那么可以抛出一个exception,而不是试图做一些“聪明”的事情。

里斯

如果发现自己处于表示编程错误的情况,并且想要停止运行应用程序,则只应该抛出exception。 因此,抛出exception的最好方法是使用NSAssert和NSParameterAssertmacros,并确保没有定义NS_BLOCK_ASSERTIONS。

示例代码案例:@throw([NSException exceptionWithName:…

 - (void)parseError:(NSError *)error completionBlock:(void (^)(NSString *error))completionBlock { NSString *resultString = [NSString new]; @try { NSData *errorData = [NSData dataWithData:error.userInfo[@"SomeKeyForData"]]; if(!errorData.bytes) { @throw([NSException exceptionWithName:@"<Set Yours exc. name: > Test Exc" reason:@"<Describe reason: > Doesn't contain data" userInfo:nil]); } NSDictionary *dictFromData = [NSJSONSerialization JSONObjectWithData:errorData options:NSJSONReadingAllowFragments error:&error]; resultString = dictFromData[@"someKey"]; ... } @catch (NSException *exception) {  NSLog( @"Caught Exception Name: %@", exception.name);  NSLog( @"Caught Exception Reason: %@", exception.reason ); resultString = exception.reason; } @finally { completionBlock(resultString); } 

}

使用:

 [self parseError:error completionBlock:^(NSString *error) { NSLog(@"%@", error); }]; 

另一个更高级的用例:

 - (void)parseError:(NSError *)error completionBlock:(void (^)(NSString *error))completionBlock { NSString *resultString = [NSString new]; NSException* customNilException = [NSException exceptionWithName:@"NilException" reason:@"object is nil" userInfo:nil]; NSException* customNotNumberException = [NSException exceptionWithName:@"NotNumberException" reason:@"object is not a NSNumber" userInfo:nil]; @try { NSData *errorData = [NSData dataWithData:error.userInfo[@"SomeKeyForData"]]; if(!errorData.bytes) { @throw([NSException exceptionWithName:@"<Set Yours exc. name: > Test Exc" reason:@"<Describe reason: > Doesn't contain data" userInfo:nil]); } NSDictionary *dictFromData = [NSJSONSerialization JSONObjectWithData:errorData options:NSJSONReadingAllowFragments error:&error]; NSArray * array = dictFromData[@"someArrayKey"]; for (NSInteger i=0; i < array.count; i++) { id resultString = array[i]; if (![resultString isKindOfClass:NSNumber.class]) { [customNotNumberException raise]; // <====== HERE is just the same as: @throw customNotNumberException; break; } else if (!resultString){ @throw customNilException; // <====== break; } } } @catch (SomeCustomException * sce) { // most specific type // handle exception ce //... } @catch (CustomException * ce) { // most specific type // handle exception ce //... } @catch (NSException *exception) { // less specific type // do whatever recovery is necessary at his level //... // rethrow the exception so it's handled at a higher level @throw (SomeCustomException * customException); } @finally { // perform tasks necessary whether exception occurred or not } 

}

目标C中没有理由不使用exception来表示业务规则exception。 苹果可以说使用NSError谁在乎。 Obj C已经有很长一段时间了,有一次,所有的C ++文档都说了同样的事情。 不pipe是多么昂贵的投掷和捕捉exception,是一个例外的寿命是非常短暂的,…它是一个例外的正常stream量。 在我的生命中,我从来没有听过有人说过,那个exception花了很长时间才被抛出去的人。

此外,有人认为目标C本身太昂贵,而代之以C或C ++。 所以总是说使用NSError是不知情的和偏执的。

但是这个线程的问题还没有被回答什么是抛出exception的最佳方式。 返回NSError的方法是显而易见的。

所以是这样的:[NSException raise:… @throw [[NSException alloc] initWithName ….或@throw [[MyCustomException …?

我在这里使用的检查/取消选中的规则与上面略有不同。

(在这里使用java隐喻)选中/取消选中的真正区别非常重要 – >是否可以从exception中恢复。 而恢复我的意思不只是不崩溃。

所以我使用自定义的exception类与@throw可恢复的exception,因为它可能我会有一些应用程序方法寻找在多个@catch块的某些types的失败。 例如,如果我的应用程序是一个ATM机,我会有一个@catch块“WithdrawalRequestExceedsBalanceException”。

我使用NSException:引发运行时exception,因为我没有办法从exception中恢复,除非在更高级别捕获并logging它。 而且,为这个创build一个自定义类没有意义。

无论如何,那是我做的,但如果有一个更好,同样富有performance力的方式,我也想知道。 在我自己的代码中,由于很久以前我停止对Hella编码,即使我通过API传递一个NSError,也不会返回NSError。