Objective-C
语言不例外的也有“异常”(exception)机制,但是与其它语言肯定存在差异。我们要重新学习一下。
首先就是,“自动引用计数”(Automatic ReferenceCounting, ARC)
在默认情况下不是“异常安全的"(exception safe)
。具体来说,这意味着:如果抛出异常,那么本应在作用域末尾释放的对象现在却不会自动释放了。如果想生成“异常安全”的代码,可以通过设置编译器的标志来实现,不过这将引入一些额外代码,在不抛出异常时,也照样要执行这部分代码。需要打开的编译器标志叫做-fobjc-arc-exceptions
。
但是我们应该注意,Objective-C语言只有在极其罕见的情况下拋出异常,异常拋出之后,无须考虑恢复问题,而且应用程序此时也应该退出。这就是说,不用再编写复杂的“异常安全”代码了。
异常只应该用于极其严重的错误,比如说,你编写了某个抽象基类,它的正确用法是先从中继承一个子类,然后使用这个子类。在这种情况下,如果有人直接使用了这个抽象基类,那么可以考虑抛出异常。
与其他语言不同,Objective-C中没办法将某个类标识为“抽象 类”。要想达成类似效果,最好的办法是在那些子类必须覆写的超类方法里抛出异常。这样的话,只要有人直接创建抽象基类的实例并使用它,即会拋出异常:
-(void)mustOverrideMethod {
NSString *reason = [NSStringstringWithFormat:
@"%@ must be overridden",
NSStringFromSelector(_cmd)];
@throw [NSException
exceptionWithName:NSInternalInconsistencyException
reason:reason
userInfo:nil];
}
既然异常只用于处理严重错误(fatal error,致命错误),那么对其他错误怎么办呢?在出 现“不那么严重的错误"(nonfatal error,非致命错误)时,Objective-C语言所用的编程范式为: 令方法返回nil/0
,或是使用NSError
,以表明其中有错误发生。例如,如果初始化方法无法根据传入的参数来初始化当前实例,那么就可以令其返回nil/0:
-(id)initWithValue:(id)value {
if ((self = [super init])){
if ( /* Value means instance can11 be created */ ) {
self = nil;
} else {
// Initialize instance
}
}
return self;
}
在这种情况下,如果if
语句发现无法用传人的参数值来初始化当前实例(比如这个方法 要求传入的value参数必须是non-nil
的),那么就把self
设置成nil
,这样的话,整个方法的 返回值也就是nil
了。调用者发现初始化方法并没有把实例创建好,于是便可确定其中发生了错误。
NSError
的用法更加灵活,因为经由此对象,我们可以把导致错误的原因回报给调用者。 NSError
对象里封装了三条信息:
Error domain(错误范围,其类型为字符串)
错误发生的范围。也就是产生错误的根源,通常用一个特有的全局变量来定义。比方说,“处理URL的子系统”(URL-handling subsystem)
在从URL中解析或取得数据时如果出错了,那么就会使用NSURLErrorDomain
来表示错误范围。Error code(错误码,其类型为整数)
独有的错误代码,用以指明在某个范围内具体发生了何种错误。某个特定范围内可能会发生一系列相关错误,这些错误情况通常采用enum
来定义。例如,当HTTP
请求出错时,可能会把HTTP
状态码设为错误码。Uesr info(用户信息,其类型为字典)
有关此错误的额外信息,其中或许包含一段“本地化的描述”(localized description)
, 或许还含有导致该错误发生的另外一个错误,经由此种信息,可将相关错误串成一条“错误链”(chain of errors)
。
NSError的一种常见用法是,经由方法的“输出参数”返回给调用者。比如像这样:
-(BOOL)doSomething: (NSError**)error
用例为:
NSError *error = nil;
BOOL ret = [object doSomething:&error];
if (error) {
//There was an error
}
也可以通过委托协议来传递此错误。有错误发生时,当前对象会把错误信息经由协议中的某个方法传给其委托对象(delegate)
。这里不做过多说明。
要点
- 只有发生了可使整个应用程序崩溃的严重错误时,才应使用异常。
- 在错误不那么严重的情况下,可以指派“委托方法”(delegate method)来处理错误,也可以把错误信息放在
NSError
对象里,经由“输出参数”返回给调用者。