博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
OC的异常机制
阅读量:3747 次
发布时间:2019-05-22

本文共 7797 字,大约阅读时间需要 25 分钟。

 的异常机制通常只作为一种程序调试,捕捉机制。 
我们先来下OC的异常机制。示例程序: 
FKEatable.h

#import 
// 定义协议@protocol FKEatable@optional- (void) taste;@end
1
2
3
4
5
6
7
1
2
3
4
5
6
7

FKApple.h

#import 
#import "FKEatable.h"// 定义类的接口部分,实现FKEatable协议@interface FKApple : NSObject
@end
1
2
3
4
5
6
1
2
3
4
5
6

FKApple.m

#import "FKApple.h"// 为FKApple提供实现部分,这里未实现taste方法@implementation FKApple@end   
1
2
3
4
5
1
2
3
4
5

FKAppleTest.m

#import "FKApple.h"int main(int argc , char * argv[]){    @autoreleasepool{        // 创建FKApple对象        FKApple* app = [[FKApple alloc] init];        // 调用taste方法        [app taste];    }}   
1
2
3
4
5
6
7
8
9
10
11
1
2
3
4
5
6
7
8
9
10
11

编译运行后,引发异常:

-[FKApple taste]: unrecognized selector sent to instance 0x7fbb60e007d02015-09-28 12:04:50.472 923[1896:64136] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[FKApple taste]: unrecognized selector sent to instance 0x7fbb60e007d0'   
1
2
1
2

使用@try…@catch…@finally 捕捉异常

为了避免前面的程序出现的异常,可以用 OC 的异常机制进行处理。 
开发者可以将可能引发异常的代码放在@try后的代码内,当程序引发异常时,该异常可以用catch进行捕捉。 
objective-c将可能出现异常的代码放在@try块中,所有的 异常处理逻辑放在@catch块中,最后用@finally 块来回收资源。 
objective-c异常处理机制的语法结构:

@try{    //业务实现代码    ...}@catch ( 异常1 ex){    // 异常处理代码        ...}@catch ( 异常2 ex){    // 异常处理代码        ...}//可能更多的@catch块...@finally{}   
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

抛出(throw)异常

如果执行@try 块里的业务逻辑代码时出现异常,系统将自动生成一个异常对象,该异常对象被提交给系统,该过程称为抛出(throw)异常。

捕获(catch)异常

当运行环境接收 到异常 对象时,会寻找能处理该异常对象的@catch 块,若找到合适的@catch 块,就把该异常 对象交给该@catch 块处理,这个过程称为捕获(catch)异常。

遇到异常退出的情形是怎么发生的

不管程序代码块是否处在@try 块内,只要执行该代码块出现了异常,,系统总会自动生成一个异常对象。如果程序没有为这段代码定义任何@catch 块 ,系统无法找到处理该异常的@catch 块,程序就此退出。

异常捕获示意图: 
这里写图片描述 
由上图可以看出,一般来说,如果@try 块被执行一次,它后面只有一个 @catch 块会被执行 ,绝不可能有多个@catch块被执行。——除非 使用了 goto 语句后又重新运行@try 块。

特别提示: 
@try块@catch 块与 if 语句不一样,后面的{ }不可以省略。另外,@try 块里声明的变量是代码块的局部变量。

改写本篇初始的示例程序的测试程序: 
ExceptionTest.m

#import "FKApple.h"int main(int argc , char * argv[]){    @autoreleasepool{        @try        {            // 创建FKApple对象            FKApple* app = [[FKApple alloc] init];            // 调用taste方法            [app taste];        }        @catch(NSException* ex)        {            NSLog(@"==捕捉异常==");            NSLog(@"捕捉异常:%@,%@" , ex.name , ex.reason);        }        @finally        {            // 此处可进行资源回收等操作            NSLog(@"资源回收!");        }        NSLog(@"程序执行完成!");    }}   
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

编译运行后,结果:

2015-09-28 14:44:15.394 923[2422:106174] -[FKApple taste]: unrecognized selector sent to instance 0x7f9a224022602015-09-28 14:44:15.450 923[2422:106174] ==捕捉异常==2015-09-28 14:44:15.450 923[2422:106174] 捕捉异常:NSInvalidArgumentException,-[FKApple taste]: unrecognized selector sent to instance 0x7f9a224022602015-09-28 14:44:15.451 923[2422:106174] 资源回收!2015-09-28 14:44:15.451 923[2422:106174] 程序执行完成!   
1
2
3
4
5
1
2
3
4
5

NSException类是 objective-c 所有异常类的基类,其他 异常都应该通过该类 进行派生。

先处理小异常再处理大异常的原则

为什么要先处理小异常,再处理大异常呢? 
拿上面的示例来说再参照异常捕获示意图,如果我们把NSException对应 的@catch 块排在其他@catch 块的前面,运行环境将直接进入该@catch 块中,排在它后面的@catch 块 将永远没有可执行的机会。——因为 所有的异常 对象都是NSException或其 子类的实例。

实际上,进行异常捕获时不仅 应该把NSException对应的@catch 块放在最后,所有父类异常的@catch 块都应该排在子类异常@catch 块的后面。原因同上。

特别注意:

先处理小异常,再处理大异常这条规则需要开发者自己保证,OC 不会给出任何提示信息。

访问异常信息

可以通过访问@catch 后 的异常形参获来访问异常对象的相关信息。 
当系统决定调用某个@catch块来处理该异常对象时,会将异常对象赋给@catch 块后的异常参数。程序可以通过该参数来获得异常的相关信息。 
所有异常对象都包含如下几个 常用方法: 
1.name: 返回该异常详细的名称; 
2.reason: 返回引发该异常的原因。 
3.userInfo: 返回该引发该异常的用户附加信息——返回一个 NSDictionary 对象。

由于这些方法相当于 getter 方法,习惯上我们常用点语法来获取这些信息。如前面示例中的结果。如果程序发生的异常被捕捉得到了正常处理,那么程序就可以继续执行,直到执行完成。

用@finally 回收资源

有时候,程序在@try块打幵了一些物理资源(例如连接、网络连接和磁盘文件 等),这些物理资源都必须显式回收。在没有使用ARC机制的怡况下,所有对象占用的内存都必须显式回收,这也必须在@finally块中完成。

为了保证一定能回收@try 块中打开的资源, @finally块总会被执行——甚至在@try,@catch块中使用了 return 语句时。

异常处理的语法结构中,只有@try 块是必须的,@catch块和@finally都是可选 的,但必须至少有一个。他们的位置,就像语法格式中那样的顺序。

通常,不要在@finally 块中使用 return 与 @throw等导致方法终止的语句

通常,不要在@finally 块中使用 return 与 @throw等导致方法终止的语句,一旦在@finally 块中使用 return 与 @throw等导致方法终止的语句,将会导致@try块以及@catch 块中的return 与 @throw语句失效。 
示例程序: 
FinallyFlowTest.m

#import "FKEatable.h"BOOL test(){    @try    {        // 因为finally块中包含了return语句,        // 所以下面的return语句失去作用        return YES;    }    @finally    {        return NO;    }}int main(int argc , char * argv[]){    @autoreleasepool{        BOOL a = test();        // 输出代表NO的0        NSLog(@"%d" , a);    }}   
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

这里写图片描述

抛出异常与自定义异常类

大部分时候,我们直接抛出 NSException 对象即可,少数情况下,OC 也可以抛出自定义异常——此时可以通过异常的类名来包含一些异常的信息。用户自定义异常都应该继承 NSException 基类,也可以为自定义异常增加一些额外的附加属性,但通常没有必要。 
如果需要在程序中自行抛出异常,应使用@throw 语句,@throw语句可以单独使用,它抛出的不是异常类,而是一个异常实例。@throw 语句的语法格式:

@throw ExceptionInstance;   
1
1

示例程序: 
FKMyException.h

#import 
// 定义类的接口部分@interface FKMyException : NSException@end
1
2
3
4
5
1
2
3
4
5

FKMyException.m

#import "FKMyException.h"// 为FKMyException提供实现部分@implementation FKMyException@end   
1
2
3
4
5
1
2
3
4
5

FKDog.h

#import 
// 定义类的接口部分@interface FKDog : NSObject@property (nonatomic , assign) int age;@end
1
2
3
4
5
6
1
2
3
4
5
6

FKDog.m

#import "FKDog.h"#import "FKMyException.h"// 为FKDog提供实现部分@implementation FKDog@synthesize age = _age;- (void) setAge:(int)age{    if(self.age != age)    {        // 检查年龄是否在0~50之间        if(age > 50 || age < 0)        {            // 手动抛出异常            @throw [[FKMyException alloc]                 initWithName:@"IllegalArgumentException"                reason:@"狗的年龄必须在0~50之间"                userInfo:nil];        }        _age = age;    }}@end   
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

FKDogTest.m

#import "FKDog.h"#import "FKMyException.h"int main(int argc , char * argv[]){    @autoreleasepool{        // 创建FKDog对象        FKDog* dog = [[FKDog alloc] init];        dog.age = 20;        NSLog(@"狗的年龄为:%d" , dog.age);        dog.age = 80;    }}   
1
2
3
4
5
6
7
8
9
10
11
12
13
1
2
3
4
5
6
7
8
9
10
11
12
13

编译运行结果:

2015-09-28 16:51:37.956 923[2601:138874] 狗的年龄为:202015-09-28 16:51:37.976 923[2601:138874] *** Terminating app due to uncaught exception 'IllegalArgumentException', reason: '狗的年龄必须在0~50之间'   
1
2
1
2
你可能感兴趣的文章
求职指南(1)
查看>>
MySQL day11
查看>>
MySQL day12
查看>>
JSONP原理
查看>>
Vue.js学习笔记—插值的操作(1)
查看>>
CSS的四种方式实现水平居中
查看>>
RISC-V生态架构浅析(认识RISC-V)
查看>>
? 精美图文带你掌握 JVM 内存布局
查看>>
谈谈go.sum
查看>>
tls 1.2 example
查看>>
GitHub 计划登陆中国,将产生哪些影响与意义?
查看>>
2019 我是怎样熬过来的?
查看>>
【C++学习计划】深入浅出——变量作用域(Day3)
查看>>
策略模式
查看>>
Spring Boot 实战 入门
查看>>
关于web系统整体优化提速总结
查看>>
分布式文件系统 - fastDFS
查看>>
BUAA OO 2019 第一单元作业总结
查看>>
格网编码查询方案在项目运用上的进一步探索
查看>>
Matlab适配器模式
查看>>