首页 文章

在objective-c / cocoa中抛出异常

提问于
浏览
401

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

13 回答

  • 33

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

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

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

    Apple's documentation for Obj-C 2.0声明如下:"Important: Exceptions are resource-intensive in Objective-C. You should not use exceptions for general flow-control, or simply to signify errors (such as a file not being accessible)"

    Apple's conceptual Exception handling documentation解释相同,但更多的话:"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()函数的一个薄包装,基本上搞乱了你仔细的内存处理,参见this explanation .

  • 8
    @throw([NSException exceptionWith…])
    
  • 6

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

  • 14

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

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

    Apple在这里解释了如何抛出和处理异常:Catching Exceptions Throwing Exceptions

  • 59

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

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

  • 0

    Use NSError to communicate failures rather than exceptions.

    关于NSError的快速点:

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

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

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

    参考链接:Reference

  • 252

    这就是我从“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];
    
  • -7

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

    @throw[NSException exceptionWithName];
    

    或第二种方法

    NSException e;
    [e raise];
    
  • 15

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

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

    里斯

  • 516

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

  • 0

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

    }

  • 7

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

    此外,有些人认为目标C本身太昂贵,而且代码用C或C代替 . 所以说永远使用NSError是不明智和偏执的 .

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

    所以它是:[NSException raise:... @throw [[NSException alloc] initWithName ....或@throw [[MyCustomException ...?

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

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

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

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

    无论如何,这就是我所做的,但如果有一种更好的,同样富有表现力的方式,我也想知道 . 在我自己的代码中,由于我很久以前就停止编写C hella,即使我通过API传递了一个NSError,也永远不会返回NSError .

相关问题