0x01 什么是异常
异常指的是程序运行中的意外事件,发生异常后,程序可以创建一个异常对象并执行特定的操作方式。
Objective-C的异常机制和C++的异常机制是兼容的。
Cocoa中使用NSException类来表示异常,我们也可以创建NSException的子类来表达自己的异常。
Cocoa要求所有的异常必须是NSException类型的异常,虽然我们可以通过其他对象来抛出异常,但不会被Cocoa处理!
0x02 处理异常的目的
处理异常的真正目的是处理程序执行中的错误。
Cocoa框架处理错误的方式通常是退出程序,但这肯定不是我们想要的。
为了找出错误的原因,我们应该抛出异常并捕捉异常。
0x03 让Xcode支持异常特性
如果想要使用异常特性,就必须在Xcode 7中开启Enable Objective-C Exceptions项。
选择项目的Build Settrings页,在搜索框中输入关键字exception,可以在编译器LLVM 7.1下拉列表中看到异常特性开关:
如果关闭异常特性支持,编译器将报错,提示异常特性相关代码不能使用:
0x04 与异常有关的关键字
异常的所有关键字都是以@开头的。
@try {
// code you want to execute that might throw an exception.
}
@catch (NSException *exception)
{
// code to execute that handles exception
}
@finally {
// code that will always be executed. Typically for cleanup.
}
@try
定义用来测试的代码块以决定是否抛出异常,出现异常则捕捉,没有异常则正常运行。
C语言中经常会在异常处理代码中使用setjmp和longjmp语句,但它们不能用于跳出@try代码块,可以用goto和return语句退出异常处理代码。
用@try建立异常检测不会产生性能消耗,但之后捕捉异常却会大量消耗系统资源并影响程序运行速度。
所以在一般的流程中不要使用异常特性。
@catch()
定义用来处理已抛出异常的代码块。
它可接收一个参数,为了确保Cocoa能正常处理异常,我们应该坚持只用NSException对象来抛出异常。
@throw
通知异常的过程通常被称为抛出异常。
通常程序在检测到异常后会抛出异常,但我们可以再次用@throw关键字来通知异常。
在catch()异常处理代码中可以重复抛出异常而无需指定异常对象。
@finally
定义无论是否有抛出异常都会被执行的代码块,该段代码始终会被执行。
示例:
//节选Car.m
//...在Car.m中加入异常处理代码
- (void)setTire:(Tire *)tire atIndex:(int)index
{
@try {
[tires replaceObjectAtIndex:index withObject:tire];
//检测数组溢出
} @catch (NSException *exception) {
NSLog(@"%@", [exception reason]);
}
} // setTire:atIndex:
//后面省略Car.m的其他代码...
//--------------------------------------------------------------------------
//main.m
int main(int argc, const char * argv[])
{
@autoreleasepool
{
Car *car = [[Car alloc] init];
for (int i = 0; i < 6; i++) //估计在for循环中让数组索引值溢出
{
AllWeatherRadial *tire;
tire = [[AllWeatherRadial alloc] init];
[car setTire:tire atIndex:i];
[tire release];
}
Engine *engine = [[Slant6 alloc] init];
[car setEngine:engine];
[car print];
[car release];
}
return 0;
}
//main.m
//--------------------------------------------------------------------------
//和异常相关的输出结果如下:
//
//-[__NSArrayM replaceObjectAtIndex:withObject:]: index 4 beyond bounds [0 .. 3]
//-[__NSArrayM replaceObjectAtIndex:withObject:]: index 5 beyond bounds [0 .. 3]
//
//...以下省略
0x05 捕捉不同类型的异常
当程序检测到了异常,会创建一个NSException实例来抛出异常。
使用NSException作为捕获条件将捕获所有异常,但我们可以只关注某一类异常。
根据需要处理的异常,我们可以定义多个@catch()代码块,处理代码应该按照从具体到抽象的顺序排序,并在最后使用一个通用的处理代码:
@try{...
} @catch (MyCustomException *custom) {...
} @catch (NSException *exception) {...
} @catch (id value) {...
} @finally {...
}
0x06 异常的内存管理
异常同样也是需要内存管理的,由于现在ARC属性默认开启,所以这里不展开讨论异常的内存管理。
只需按ARC的代码规则编写代码即可。