在objective-c/cocoa中抛出异常的最佳方法是什么?


当前回答

@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

}

}

其他回答

There is no reason not to use exceptions normally in objective C even to signify business rule exceptions. Apple can say use NSError who cares. Obj C has been around a long time and at one time ALL C++ documentation said the same thing. The reason it doesnt matter how expensive throwing and catching an exception is, is the lifetime of an exception is exceedingly short and...its an EXCEPTION to the normal flow. I have never heard anyone say ever in my life, man that exception took a long time to be thrown and caught.

还有,有些人认为objective C本身太昂贵,而改用C或c++编写代码。所以说总是使用NSError是无知和偏执的。

但是这篇文章的问题还没有得到回答,抛出异常的最佳方式是什么。返回NSError的方法很明显。

[NSException raise:…]@throw [[NSException alloc] initWithName.... 或者@throw [[MyCustomException…]?

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

checked/unchecked之间的真正区别(此处使用java隐喻)非常重要——>您是否可以从异常中恢复。所谓恢复,我指的不仅仅是不崩溃。

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

我使用NSException:raise用于运行时异常,因为我没有办法从异常中恢复, 除了在更高的级别捕获它并记录它。而且没有必要为此创建一个自定义类。

无论如何,这就是我所做的,但如果有更好的,类似的表达方式,我也想知道。在我自己的代码,因为我停止编码C海拉很久以前,我从来没有返回一个NSError,即使我通过一个API传递一个。

Regarding [NSException raise:format:]. For those coming from a Java background, you will recall that Java distinguishes between Exception and RuntimeException. Exception is a checked exception, and RuntimeException is unchecked. In particular, Java suggests using checked exceptions for "normal error conditions" and unchecked exceptions for "runtime errors caused by a programmer error." It seems that Objective-C exceptions should be used in the same places you would use an unchecked exception, and error code return values or NSError values are preferred in places where you would use a checked exception.

使用NSError来传达失败而不是异常。

关于NSError的一些要点:

NSError allows for C style error codes (integers) to clearly identify the root cause and hopefully allow the error handler to overcome the error. You can wrap error codes from C libraries like SQLite in NSError instances very easily. NSError also has the benefit of being an object and offers a way to describe the error in more detail with its userInfo dictionary member. But best of all, NSError CANNOT be thrown so it encourages a more proactive approach to error handling, in contrast to other languages which simply throw the hot potato further and further up the call stack at which point it can only be reported to the user and not handled in any meaningful way (not if you believe in following OOP's biggest tenet of information hiding that is).

参考链接:参考

我认为你永远不应该使用异常来控制正常的程序流程。但是,当某些值与所需值不匹配时,应该抛出异常。

例如,如果某个函数接受一个值,并且该值永远不允许为nil,那么就可以抛出一个异常,而不是尝试做一些“聪明”的事情……

Ries

这里有一个警告。在Objective-C中,与许多类似的语言不同,您通常应该尽量避免在正常操作中可能发生的常见错误情况下使用异常。

Apple的Obj-C 2.0文档中写道:“重要的是:异常在Objective-C中是资源密集型的。您不应将异常用于一般的流控制,或仅用于表示错误(例如文件不可访问)”

Apple's conceptual Exception handling documentation explains the same, but with more words: "Important: You should reserve the use of exceptions for programming or unexpected runtime errors such as out-of-bounds collection access, attempts to mutate immutable objects, sending an invalid message, and losing the connection to the window server. You usually take care of these sorts of errors with exceptions when an application is being created rather than at runtime. [.....] Instead of exceptions, error objects (NSError) and the Cocoa error-delivery mechanism are the recommended way to communicate expected errors in Cocoa applications."

这样做的部分原因是坚持Objective-C中的编程习惯(在简单的情况下使用返回值,在更复杂的情况下使用按引用参数(通常是NSError类)),部分原因是抛出和捕获异常的代价要高得多,最后(可能是最重要的)Objective-C异常是C的setjmp()和longjmp()函数的一个瘦包装,本质上混乱了你仔细的内存处理,参见下面的解释。