美文网首页
高效编写代码的方法(十七):理解OC的错误模式

高效编写代码的方法(十七):理解OC的错误模式

作者: 蜂猴 | 来源:发表于2016-05-30 18:26 被阅读398次

    前言

    许多现代化的语言,包括OC,都包含了断言。如果你对Java的这一套错误处理比较熟悉,那么可能在OC中需要重新学习一遍OC的错误处理。

    Exception

    在ARC中,虽然我们把对象的内存管理都交给系统去处理,但是即使这样还是有可能会出现内存泄露的情况。
    其中一种对象不会释放的情况就是throw一个NSException的情况。
    防止这样的情况发生可以通过设置编译器来避免:** -fobj-arc-exceptions **
    如此一来,编译器会自动生成代码来释放exception。但是会引发的额外情况就是,对于没有throw一个exceptions时,编译器也会生成很多多余无用的代码。

    即使是在MRC中,当碰上exception时候,管理对象内存也会出现一点小问题。
    比如下面这段代码:

        id someObject = /*...*/;
        if (/**checkForError*/) {
            @throw [NSException exceptionWithName:@"ExceptionName" reason:@"Ops" userInfo:nil];
        }
        [someObject doSomething];
        [someObject release];
    

    [someObject release]这句代码就根本不会执行。
    不过估计现在也没有人使用MRC了,解决办法也很简单,就是把[someObject release]这句话放到throw前面。
    不过一旦系统中抛出的错误多起来,情况复杂起来,很可能会忽视了这一点。

    关于NSException的使用,我们应该只在出现致命的错误时才使用它来抛出异常。
    打个比方,在OC中,当我们想定义一个抽象类的时候,我们可以使用一些NSException。因为在OC中,并没有关于抽象类的定义,我们在创建新类的时候并不能通过某些关键字来使它成为一个抽象类。因此我们只能使用NSException来强制使用者来认为它是一个抽象类,比如我们强制使用者需要重写其子类中的方法的话:

    - (void)mustOverrideMethod{
        NSString *reason = [NSString stringWithFormat:@"%@ must be overriden",NSStringFromSelector(_cmd)];
        @throw  [NSException exceptionWithName:NSInternalInconsistencyException reason:reason userInfo:nil];
    }
    

    总而言之,NSException是用在致命错误上,终结App运行时候用的,并且最好能确保通过reason,开发者能够准确知道错误出现在了哪里。

    NSError

    NSError相对NSException来说,提供了更多关于错误的信息。因为它支持在运行中追踪查看NSError的详细信息。NSError包含了下面三个关键属性:

    • 1 Error domain(String类)
      用来定义错误所属的域。通常是一个全局的String变量。比如处理URL的方法就会使用NSURLErrorDomain来作为这些错误的域。
    • 2 Error code(Integer类)
      可以具体明确该错误的种类。最常见的就会网络状态错误码(200,404等等),可以用来表示某个明确错误。在OC中使用错误码时,建议使用枚举,提高可读性。
    • 3 User info
      包含一些更加具体的错误信息。

    关于NSError,非常常见的一种使用是在protocol中,比如NSURLConnectionDelegate中的一个方法:

    -  (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
    

    当一个网络连接发生异常,比如连接超时的时候,该方法就会被调用用来处理这些特殊的错误情况。

    另一个经常使用NSError的地方是NSError作为外部传入的参数的方法,比如:

    -  (BOOL)doSomeThing:(NSError **)error
    

    这个传入的error是一个指向error对象的指针。这样我们就能在方法外部去做一些error判断的工作,如下:

        NSError *error = nil;
        BOOL ret = [self doSomeThing:&error];
        if (error) {
            /**do someThing*/
        }
    

    在ARC模式下,在方法签名过程中,编译器会把NSError** 转换为NSError__autoreleasing。这意味着这个error对象在方法结束之后也会被释放。
    关于- (BOOL)doSomeThing:(NSError **)error的内部实现可以如下:

    
    - (BOOL)doSomeThing:(NSError **)error
    {
        //Do something that may cayse an error
        if (/**there was an error*/) {
            if (error) {
                //Pass the 'error' through the out-parameter
                *error = [NSError errorWithDomain:domain code:code userInfo:userinfo];
            }
            return NO;
        }else{
            return YES;
        }
    }
    

    在真正创建error对象之前,我们需要判断以下传入的error参数是否为空。
    如果不判断,直接对空指针操作会崩溃。

    前面也已经提到过,就是domain和code的使用。
    domain最好采用全局的字符串常量,用来区分error域。而code尽量使用枚举,提高可读性。

    总结

    • 1 用NSException来处理那些会崩溃整个App的致命错误。
    • 2 对于非致命错误,提供一个代理来让使用者处理错误,或者写一个外传错误参数的方法。

    相关文章

      网友评论

          本文标题:高效编写代码的方法(十七):理解OC的错误模式

          本文链接:https://www.haomeiwen.com/subject/hwccdttx.html