iOS 错误处理 (一)

错误处理恐怕是开发过程中最让人难受的环节之一。在正常情况下让程序的各部分都正常工作已经是比较困难的了,一个好的应用应该在出错时也有得体的表现。

1. 错误处理模式

三种模式

       1.1预期错误

                      磁盘空间不足是一种预期错误,虽然它极少发生,iTunes在用户设备中装满了音乐文件,这种情况很容易发生。当无法写入文件时,需要能够优雅的恢复

                      对于那些会预期发生的错误,应该得体的进行处理,不应该让程序崩溃。处理这种错误,比较通用的方式是返回一个NSError对象的引用

       1.2程序错误

     -(void)doSomething:(NSUInteger)index

{

         if (index> self.maxIndex)

         {

            return;

          }

}

传入一个超出有效范围的索引值是一种编程错误,这段代码默默忍受了这种错误,没有做出任何操作。这种问题非常难于调试。应该传入有效的值,这是方法调用者的责任。默默忽略错误值是NSArray能做的最糟糕的事情,让程序崩溃会是一个更好的选择

1.3非预期错误

                      在iOS中,分配小块内存失败是一种非预期错误,在正常操作中永远不该发生。在遇到这个错误之前,程序应该早就收到内存预警消息并且被强制结束了。

     NSString * string =[NSString stringWithFormat:@"%d",1];

NSArray * array = [NSArray arrayWithObject:string];

           由于Foundation无法正确分配内存而导致stringWithFormat:方法失败,这种情况是可能发生的。这样会导致程序会抛出一个试图向数组中插入nil”的异常,最后很可能导致应用崩溃。C语言编程一般会加一个检查来防止这种情况发生,iOS不需要这样。

通常情况下可以忽略非预期错误,直接让程序崩溃就好了。理由:发生概率极小;保持代码简洁;OC中为内存分配错误处理代码几乎是没有意义。

 

 

2.    断言

使用断言可以有效的防止程序错误。断言要求程序中特定的语句必须为真。如果不为真,说明程序正处于一种无法预测的状态,这时候程序不应该继续执行下去。

NSAssert( index != 0 , @"indexmust not be zero");

 

[self doSomething:0];

控制台打印:

2013-07-13 10:31:14.466 test[8796:c07] *** Assertionfailure in -[WGQ_ViewController doSomething:],/Users/king/Desktop/test/test/WGQ_ViewController.m:26

2013-07-13 10:31:14.467 test[8796:c07] *** Terminatingapp due to uncaught exception 'NSInternalInconsistencyException', reason:'index must not be zero'

    如果测试条件返回NO,NSAssert 就会抛一个异常,异常处理程序捕获异常之后,会调用abort结束程序。 并显示自定义的消息"index must not be zero"

并同时显示出错文件,出错代码,调用函数等消息。他是一个程序跟踪的很好的手段。(C语言中使用断言NSCAssert.

    MAC开发,出现异常只会结束当前循环,iOS中,结束整个程序。

    禁用NSAssert:  targets – build setting –APPLE LLVM—release 中添加NS_BLOCK_ASSERTIONS.

               XCode 4.6.3 默认情况下会禁用发布版本代码中得断言。

    用法总结与注意事项:

1)在函数开始处检查传入参数的合法性

      2) 每个NSAssert 只检验一个条件,因为检验多个条件时,当断言失败,无法直观的判断是哪个条件失败

      3)不能使用改变环境的语句,因为assert只在DEBUG个生效,如果这么做,会使用程序在真正运行时遇到问题
错误:

         assert(i++< 100)
这是因为如果出错,比如在执行之前i=100,那么这条语句就不会执行,那么i++这条命令就没有执行。


      正确: assert(i < 100)
 ;

            i++;

      4) assert和后面的语句应空一行,以形成逻辑和视觉上的一致感

      5)有的地方,assert不能代替条件过滤

    下面的代码是吧NSCAssert 包装为RNCAssert 在OC中使用断言的时候就应该使用NSAssert。

#define RNLogBug NSLog

// RNAssert and RNCAssert work exactly like NSAssert andNSCAssert

// except they log, even in release mode

 

#define RNAssert(condition, desc, ...) \

  if (!(condition)){ \

   RNLogBug((desc), ## __VA_ARGS__); \

    NSAssert((condition),(desc), ## __VA_ARGS__); \

  }

#define RNCAssert(condition, desc) \

  if (!(condition)){ \

   RNLogBug((desc), ## __VA_ARGS__); \

   NSCAssert((condition), (desc), ## __VA_ARGS__); \

  }

断言应该位于导致程序崩溃的代码之前。看下面的例子

               RNAssert(foo !=nil,@"foo must not be nil");

  [array addObject:foo];

如果这里会导致断言失败,那么关闭断言,程序依然会崩溃。所以要将代码改为下面这样:

    RNAssert(foo != nil, @"foomust not be nil");

    if (foo != nil) {

        [array addObject:foo];

}

这样就好了,RNAssert 可以记录日志。但是代码有冗余,如果断言条件和if条件不匹配,就可能产生bug

    if (foo != nil)

    {

        [arrayaddObject:foo];

    }

    else

    {

       RNAssert(NO, @"foo must not be nil");

     }

     这样就保证了断言条件跟if条件总是匹配的。这是一种比较好的断言使用方式。另外建议在switch语句的defualt分支中使用断言。

     assert的缺点是,频繁的调用会极大的影响程序的性能,增加额外的开销。

     插一点关于调试信息—几个宏 (附加内容):

       1)__VA_ARGS__ 是一个可变参数的宏,很少人知道这个宏,这个可变参数的宏是新的C99规范中新增的,目前似乎只有gcc支持(VC6.0的编译器不支持)。宏前面加上##的作用在于,当可变参数的个数为0时,这里的##起到把前面多余的","去掉的作用,否则会编译出错, 你可以试试。
  

       2) __FILE__ 宏在预编译时会替换成当前的源文件名
  

       3) __LINE__宏在预编译时会替换成当前的行号
  

       4)__FUNCTION__宏在预编译时会替换成当前的函数名称

3.    异常

        简单的说,在OC中,异常不是用来处理那些可恢复的错误的。异常是用来处理那些永远不应该发生却却发生了的错误,而这个时候应该结束程序运行。异常跟NSAssert比较像,事实上,NSAssert 就是作为异常实现的。

[NSException raise:@"WebService error" format:@"%@",@"测试111111"];

 

程序抛出异常的原因多种多样,可由硬件导致也可由软件引起。异常的例子很多,包括被零除、下溢和上异之类的数学错误,调用未定义的指令(例如,试图调用一个没有定义的方法 )以及试图越界访问群体中的元素

网上找的例子:

NSException* ex = [[NSExceptionalloc]initWithName:@"MyException" reason:@"b==0" userInfo:nil];

    @try

    {

        int b = 0;

        switch (b)

        {

            case 0:

               @throw(ex);//b=0,则抛出异常;

                break;

            default:

                break;

        }

    } 

    @catch (NSException *exception)//捕获抛出的异常

    {

        NSLog(@"%@~~~%@",exception.name,exception.reason);

        NSLog(@"b==0 Exception!");

    }

    @finally

    {

          NSLog(@"finally!");

       }

    [exrelease];

 

        典型应用:

        @try {

             a = UIApplicationMain(argc, argv,nil,NSStringFromClass([PGAppDelegate class]));

        }

        @catch(NSException *exception) {

           NSLog(@"Caught %@%@", [exception name], [exception reason]);

           NSLog(@"Exception - %@",[exception callStackSymbols]);

           

           exit(EXIT_FAILURE);

        }

       return a;

    以下内容出自 Exception Programming Topics :
       Cocoa框架通常不是异常安全的。异常只用来处理程序员犯的错误,程序捕获到这种异常之后应该尽快退出运行。

    在OC中,ARC默认情况不是异常安全的,有可能是因为异常而产生严重的内存泄露。理论上来说,OC++ 中得ARC是异常安全的。但是@autoreleasepool块任然可能导致后台线程发生内存泄露。使用异常安全的ARC会导致性能下降,这也是应该避免大量使用OC++的原因之一。在Clang中,制定-fobjc-arc-exception 编译标志就可使用异常安全的ARC。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值