何为异常?
我的简单理解是:程序可以编译、运行,但是在某些特定的执行环境下会导致程序运行终止,也就是我们常说的崩溃或者闪退现象。
如何处理?
我们要抓住这个导致程序运行终止的代码,然后采取合理的方式将其处理掉。Objective-C提供了异常的处理机制
@try { <#Code that can potentially throw an exception#> } @catch (NSException *exception) { <#Handle an exception thrown in the @try block#> }
有如下例子:
1、接下来,我们定义一个Person类
// // Person.h // MyObjective-C // // Created by ZhaiKun on 2017/10/11. // Copyright © 2017年 ZhaiKun. All rights reserved. // #import <Foundation/Foundation.h> @interface Person : NSObject - (void)run; @end
在Person类中声明了一个run方法
2、再看Person类的实现
// // Person.m // MyObjective-C // // Created by ZhaiKun on 2017/10/11. // Copyright © 2017年 ZhaiKun. All rights reserved. // #import "Person.h" @implementation Person @end
在Person类的实现代码中,没有实现run方法
3、接下来在程序的入口函数中对run方法进行调用
// // main.m // ExceptionDeal 异常处理 // // Created by ZhaiKun on 2017/10/11. // Copyright © 2017年 ZhaiKun. All rights reserved. // #import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { Person *person = [Person new]; [person run]; NSLog(@"程序执行到了这里"); return 0; }
程序运行到[person run]时,就终止了运行,调试信息显示:run方法声明了,但是没有实现
2017-10-11 10:03:53.177952+0800 ExceptionDeal[812:23032] -[Person run]: unrecognized selector sent to instance 0x100406730 2017-10-11 10:03:53.178930+0800 ExceptionDeal[812:23032] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Person run]: unrecognized selector sent to instance 0x100406730' *** First throw call stack: ( 0 CoreFoundation 0x00007fffb6f012cb __exceptionPreprocess + 171 1 libobjc.A.dylib 0x00007fffcbd1948d objc_exception_throw + 48 2 CoreFoundation 0x00007fffb6f82f04 -[NSObject(NSObject) doesNotRecognizeSelector:] + 132 3 CoreFoundation 0x00007fffb6e73755 ___forwarding___ + 1061 4 CoreFoundation 0x00007fffb6e732a8 _CF_forwarding_prep_0 + 120 5 ExceptionDeal 0x0000000100000f26 main + 70 6 libdyld.dylib 0x00007fffcc5ff235 start + 1 7 ??? 0x0000000000000001 0x0 + 1 ) libc++abi.dylib: terminating with uncaught exception of type NSException (lldb)
4、对这个异常进行处理,使程序可以继续运行
// // main.m // ExceptionDeal 异常处理 // // Created by ZhaiKun on 2017/10/11. // Copyright © 2017年 ZhaiKun. All rights reserved. // #import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { Person *person = [Person new]; @try { //将可能发生异常的代码写在@try块中 [person run];//一旦发生异常,立即执行@catch块中的代码,不会执行@try块中发生异常代码处之后的代码 NSLog(@"我是处于异常后面的代码"); } @catch (NSException *exception) { //确定异常已经发生,执行@catch块中的代码。如果@try块中的代码没有发生异常,是不会执行@catch块中的代码的 NSLog(@"这里是处理异常"); } NSLog(@"程序执行到了这里"); return 0; }
程序运行结果:
2017-10-11 10:22:36.727478+0800 ExceptionDeal[913:27826] -[Person run]: unrecognized selector sent to instance 0x1002032b0 2017-10-11 10:22:36.728071+0800 ExceptionDeal[913:27826] 这里是处理异常 2017-10-11 10:22:36.728109+0800 ExceptionDeal[913:27826] 程序执行到了这里 Program ended with exit code: 0
5、验证没有异常时不会执行@catch块中的代码,实现Person类中的run方法
// // Person.m // MyObjective-C // // Created by ZhaiKun on 2017/10/11. // Copyright © 2017年 ZhaiKun. All rights reserved. // #import "Person.h" @implementation Person - (void)run{ NSLog(@"实现了run方法"); } @end
程序运行结果:
2017-10-11 10:29:33.763648+0800 ExceptionDeal[967:29474] 实现了run方法 2017-10-11 10:29:33.763909+0800 ExceptionDeal[967:29474] 我是处于异常后面的代码 2017-10-11 10:29:33.763935+0800 ExceptionDeal[967:29474] 程序执行到了这里 Program ended with exit code: 0
在@try块中,没有异常时,依次执行了@try块中的所有代码,直接跳过了@catch块
6、@catch块参数说明
@catch (NSException *exception) { }
exception 指针对象,通过它可以拿到发生异常的原因。添加打印异常信息的代码
@catch (NSException *exception) { //确定异常已经发生,执行@catch块中的代码。如果@try块中的代码没有发生异常,是不会执行@catch块中的代码的 NSLog(@"这里是处理异常"); NSLog(@"发生异常的原因:%@", exception); }
程序运行结果:
2017-10-11 10:45:46.946998+0800 ExceptionDeal[1044:33191] -[Person run]: unrecognized selector sent to instance 0x100504590 2017-10-11 10:45:46.947636+0800 ExceptionDeal[1044:33191] 这里是处理异常 2017-10-11 10:45:46.947709+0800 ExceptionDeal[1044:33191] 发生异常的原因:-[Person run]: unrecognized selector sent to instance 0x100504590 2017-10-11 10:45:46.947741+0800 ExceptionDeal[1044:33191] 程序执行到了这里 Program ended with exit code: 0
7、@finally处理机制说明:位于@try{ }@catch{ }的后面
@try { <#Code that can potentially throw an exception#> } @catch (NSException *exception) { <#Handle an exception thrown in the @try block#> } @finally { <#Code that gets executed whether or not an exception is thrown#> }
在入口函数中增加@finally块
// // main.m // ExceptionDeal 异常处理 // // Created by ZhaiKun on 2017/10/11. // Copyright © 2017年 ZhaiKun. All rights reserved. // #import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { Person *person = [Person new]; @try { //将可能发生异常的代码写在@try块中 [person run];//一旦发生异常,立即执行@catch块中的代码,不会执行@try块中发生异常代码处之后的代码 NSLog(@"我是处于异常后面的代码"); } @catch (NSException *exception) { //确定异常已经发生,执行@catch块中的代码。如果@try块中的代码没有发生异常,是不会执行@catch块中的代码的 NSLog(@"这里是处理异常"); NSLog(@"发生异常的原因:%@", exception); }@finally { //无论@try块中是否有异常,都会执行这里的代码 NSLog(@"I am finally!"); } NSLog(@"程序执行到了这里"); return 0; }
模拟发生异常时的运行结果:
2017-10-11 10:57:44.240396+0800 ExceptionDeal[1141:36516] -[Person run]: unrecognized selector sent to instance 0x100202330 2017-10-11 10:57:44.241212+0800 ExceptionDeal[1141:36516] 这里是处理异常 2017-10-11 10:57:44.241259+0800 ExceptionDeal[1141:36516] 发生异常的原因:-[Person run]: unrecognized selector sent to instance 0x100202330 2017-10-11 10:57:44.241286+0800 ExceptionDeal[1141:36516] I am finally! 2017-10-11 10:57:44.241301+0800 ExceptionDeal[1141:36516] 程序执行到了这里 Program ended with exit code: 0
模拟没有异常出现时的运行结果:
2017-10-11 10:58:37.252254+0800 ExceptionDeal[1177:36918] 实现了run方法 2017-10-11 10:58:37.252422+0800 ExceptionDeal[1177:36918] 我是处于异常后面的代码 2017-10-11 10:58:37.252447+0800 ExceptionDeal[1177:36918] I am finally! 2017-10-11 10:58:37.252465+0800 ExceptionDeal[1177:36918] 程序执行到了这里 Program ended with exit code: 0
得出结论:无论在@try块中是否有异常,程序最终都会执行@finally块中的代码
注意:@try、、、@catch对异常的处理是有一定的局限性的,也就是说这个东东是不能处理所有的异常的,典型的例子就是做除法运算时分母为0的情况。那么我们又怎么样对它不能处理的异常进行处理呢?推荐的方法是:进行逻辑判断处理。