Answers:
我使用[NSException raise:format:]
如下:
[NSException raise:@"Invalid foo value" format:@"foo of %d is invalid", foo];
请注意这里。与许多类似的语言不同,在Objective-C中,您通常应避免对正常操作中可能发生的常见错误情况使用异常。
Apple的Obj-C 2.0文档指出:“重要:Objective-C中的异常是资源密集型。您不应将异常用于一般的流控制,或仅用于表示错误(例如无法访问文件)”
Apple的概念性异常处理文档对此进行了解释,但使用了更多的词语:“重要:您应保留将异常用于编程或意外的运行时错误,例如越界访问集合,尝试使不可变对象发生突变,发送无效消息等通常会在创建应用程序时而不是在运行时处理这些带有异常的错误,这些错误带有异常,而不是异常,而是错误对象(NSError)和可可错误传递机制是在可可应用程序中传达预期错误的推荐方法。”
这样做的原因部分是为了遵守Objective-C中的编程习惯用法(在简单情况下使用返回值,在更复杂情况下使用by-reference参数(通常是NSError类)),部分原因是抛出和捕获异常会更加昂贵并且最后(也是最重要的一点),Objective-C异常是对C的setjmp()和longjmp()函数的一个简单包装,本质上搞乱了您仔细的内存处理,请参阅以下说明。
@throw([NSException exceptionWith…])
Xcode将@throw
语句像语句一样识别为函数出口点return
。使用该@throw
语法可避免从中获得错误的“ 控制可能会到达非空函数结束 ”的警告[NSException raise:…]
。
另外,@throw
可用于抛出非NSException类的对象。
关于[NSException raise:format:]
。对于那些来自Java背景的人,您会记得Java区分Exception和RuntimeException。Exception是一个已检查的异常,而RuntimeException是未检查的。尤其是Java建议将检查异常用于“正常错误条件”,而将检查异常用于“由程序员错误引起的运行时错误”。似乎应该在与使用未经检查的异常相同的地方使用Objective-C异常,并且在要使用检查的异常的地方首选使用错误代码返回值或NSError值。
使用NSError传达失败而不是例外。
关于NSError的简要要点:
NSError允许C样式错误代码(整数)清楚地标识根本原因,并希望允许错误处理程序克服错误。您可以非常轻松地将来自诸如SQLite之类的C库的错误代码包装在NSError实例中。
NSError还具有成为对象的优点,并提供了一种使用其userInfo字典成员更详细地描述错误的方法。
但最重要的是,不能抛出NSError,因此它鼓励采用更主动的错误处理方法,而与其他语言相比,其他语言只是将热点土豆扔到越来越远的调用堆栈中,此时只能将其报告给用户,并且不会以任何有意义的方式进行处理(如果您相信遵循OOP的最大信息隐藏原则,那就不会)。
参考链接: 参考
仅当发现自己处于指示编程错误的状况并且想要停止应用程序运行时,才应该引发异常。因此,引发异常的最佳方法是使用NSAssert和NSParameterAssert宏,并确保未定义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中正常使用异常,甚至不表示业务规则异常。苹果可以说使用NSError谁在乎。Obj C已经存在很长时间了,一次所有C ++文档都说了同样的话。引发和捕获异常的代价无关紧要的原因是,异常的生存期非常短,并且...异常超出了正常流程。我从来没有听过有人说过这样的话,那个人花了很长时间才被抛出并抓住。
而且,有些人认为目标C本身太昂贵了,而是用C或C ++编写代码。所以说总是使用NSError是不明智和偏执的。
但是尚未解决此线程的问题是引发异常的最佳方法是什么。返回NSError的方法很明显。
就是这样:[NSException提高:... @throw [[NSException分配] initWithName ....或@throw [[MyCustomException ...?
我在这里使用的选中/未选中规则与上面略有不同。
(这里使用java隐喻)选中/未选中之间的真正区别很重要->是否可以从异常中恢复。通过恢复,我的意思是不仅不会崩溃。
因此,我将自定义异常类与@throw一起用于可恢复的异常,因为我可能会使用一些app方法在多个@catch块中查找某些类型的故障。例如,如果我的应用程序是ATM机,则对于“ WithdrawalRequestExceedsBalanceException”,我将有一个@catch块。
我将NSException:raise用于运行时异常,因为我无法从异常中恢复,只能在更高级别捕获并记录它。为此没有必要创建自定义类。
无论如何,这就是我的工作,但如果我有更好的,类似的表达方式,我也想知道。在我自己的代码中,因为很久以前我停止对C进行编码,所以即使我通过API传递了NSError,也永远不会返回NSError。
@throw([NSException exceptionWith…])
因为它更简洁。