提问



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

最佳参考


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


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

其它参考1


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


Apple的Obj-C 2.0文档声明如下:重要:在Objective-C中,例外是资源密集型的。您不应该将异常用于一般流量控制,或仅仅用于表示错误(例如文件无法访问)[11]


Apple的概念异常处理文档解释相同,但更多的话:重要:您应该保留使用异常编程或意外的运行时错误,例如越界集合访问,尝试改变不可变对象,发送一个无效的消息,并失去与窗口服务器的连接。在创建应用程序时而不是在运行时,通常会使用异常处理这些类型的错误。 [[.....]]而不是异常,错误对象(NSError)和Cocoa错误传递机制是在Cocoa应用程序中传达预期错误的推荐方法。[12]


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

其它参考2


@throw([NSException exceptionWith…])

其它参考3


我没有代表对eJames的评论做出回应,所以我想我需要把它放在这里。对于那些来自Java背景的人,您会记得Java区分Exception和RuntimeException。异常是已检查的异常,并且未选中RuntimeException。特别是,Java建议对正常错误条件使用已检查的异常,并为程序员错误导致的运行时错误使用未经检查的异常。似乎应该在使用未经检查的异常的相同位置使用Objective-C异常,并且在使用已检查异常的位置首选错误代码返回值或NSError值。

其它参考4


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


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


Apple在这里解释了如何抛出和处理异常:
捕捉异常
抛出异常[14] [15]

其它参考5


从ObjC 2.0开始,Objective-C异常不再是C的setjmp()longjmp()的包装,并且与C ++异常兼容,@ try是免费的,但抛出和捕获异常更加昂贵。


无论如何,断言(使用NSAssert和NSCAssert宏系列)抛出NSException,并且理所当然地将它们用作Ries状态。

其它参考6


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


关于NSError的快速点:



  • NSError允许C样式错误代码(整数)清楚地标识根本原因,并希望允许错误处理程序克服错误。您可以非常轻松地从NSError实例中的SQLite中包装C库中的错误代码。

  • NSError还具有作为对象的优点,并提供了一种使用其userInfo字典成员更详细地描述错误的方法。

  • 但最重要的是,NSError不能被抛出,所以它鼓励采用更主动的错误处理方法,与其他语言相比,这些语言只是将热门土豆进一步向上调整堆栈,此时它只能报告给用户并没有以任何有意义的方式处理(如果你相信遵守OOP的最大信息隐藏原则)。



参考链接:参考[16]

其它参考7


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


@throw[NSException exceptionWithName];


或第二种方法


NSException e;
[e raise];

其它参考8


这就是我从The Big Nerd Ranch Guide(第4版)中学到的:


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

其它参考9


我相信你永远不应该使用Exceptions来控制正常的程序流程。但是,只要某些值与期望值不匹配,就应该抛出异常。


例如,如果某个函数接受一个值,并且该值永远不允许为nil,则可以设置异常而不是尝试做一些聪明的事情......


里斯

其它参考10


如果您发现自己处于指示编程错误的情况并且想要停止运行应用程序,则应该只抛出异常。因此,抛出异常的最佳方法是使用NSAssert和NSParameterAssert宏,并确保未定义NS_BLOCK_ASSERTIONS。

其它参考11


案例的示例代码:@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

}


}

其它参考12


没有理由不在目标C中正常使用例外来表示业务规则例外。 Apple可以说使用NSError谁在乎。 Obj C已经存在了很长时间,同时所有C ++文档也说了同样的事情。抛出和捕获异常的成本并不重要的原因是,异常的生命周期非常短......并且它是正常流程的例外。在我的生命中,我从来没有听过任何人说过,那个例外需要很长时间才能被抛出并被抓住。


此外,有些人认为目标C本身太昂贵,而且用C或C ++编写代码。因此,总是使用NSError是不明智和偏执的。


但是这个问题的问题还没有得到解决,这是抛出异常的最好方法。返回NSError的方法很明显。


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


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


检查/取消选中(使用java隐喻)之间的真正区别很重要 - >是否可以从异常中恢复。通过恢复我的意思不仅仅是不崩溃。


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


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


无论如何,这就是我所做的,但如果有一个更好的,同样富有表现力的方式我也想知道。在我自己的代码中,因为很久以前我停止编码C hella我即使通过了一个我也没有返回NSError通过API。