何为异常?

我的简单理解是:程序可以编译、运行,但是在某些特定的执行环境下会导致程序运行终止,也就是我们常说的崩溃或者闪退现象。

如何处理?

我们要抓住这个导致程序运行终止的代码,然后采取合理的方式将其处理掉。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的情况。那么我们又怎么样对它不能处理的异常进行处理呢?推荐的方法是:进行逻辑判断处理。