“好记性不如烂笔头”,虽然我不是用的笔,但是敲一遍效果对我来说肯定更好!!!
首先要注意的是“自动引用计数”在默认情况下不是“异常安全的”。具体来说,这意味着:如果抛出异常,那么本应该在作用域末尾释放的对象现在却不会自动释放了。即使不使用ARC,也很难写出在抛出异常时不会导致内存泄露的代码。
Object-C现在所采用的办法是:只在极其罕见的情况下抛出异常,异常抛出之后无须考虑恢复问题,而且应用程序在此时也应该退出。这就是说不用再编写复杂的“异常安全”代码了。
比如说你写了一个抽象基类,它的正确用法是先从中继承一个子类,然后再使用这个子类。在这种情况下,如果有人直接使用了这个抽象基类,那么可以抛出异常。与其他语言不同,OC中没有办法将某个类标示为“抽象类”。要想达成类似效果,最好的办法就是在那些子类必须覆写的父类方法里面抛出异常。
异常只用于处理严重的错误(fatal error,致命错误),在其他不严重的或者说不致命的错误中,OC所采取的编程范式为:令方法返回nil/0,或者是使用NSError,以表明有错误发生。比如根据传入的参数无法初始化当前实例,那么就返回nil/0:
在这种情况下,如果if语句发现无法用传入的参数值来初始化当前实例,那么就把self设置为nil,这样的话整个方法的返回值也就是nil了。
NSError的用法更加灵活,因为经由此对象,我们可以把导致错误的原因回报给调用者。
NSError对象里封装了三条信息:
Error domain(错误范围,其类型为字符串):错误发生的范围。也就是产生错误的根源,通常用一个特定的全局变量来定义。比方说:“处理URL的子系统”在从URL中解析或取得数据时如果出错了,那么就会使用NSURLErrorDomain来表示错误范围。
Error code(错误码,其类型为整数):独有的错误代码,用以指明某个范围内具体发生了何种错误。某个特定范围内可能会发生一系列相关错误,这些错误情况通常采用enum来定义。例如,当HTTP请求出错时,可能会把HTTP状态码设置为错误码!
User info(用户信息,其类型为字典):有关此错误的额外信息,其中或许包含一段“本地化的描述”或许还含有导致该错误发生的另外一个错误,经由此种信息,可将相关错误串成一条“错误链”。
在设计API时,NSError的第一种常见用法是通过委托协议在传递此错误。有错误发生时,当前对象会把错误信息经由协议中的某个方法传给其委托对象(delegate)。例如:NSURLConnection在其委托协议NSURLConnectionDelegate之中就定义了如下方法:
当NSURLConnection出错之后(比如超时了),就会调用此方法以处理相关错误。这个方法不是调用者必须实现的,但是比抛出异常要好,因为调用者至少可以自己决定是否需要NSURLConnection回报此错误。
NSError的另外一种常见用法是:经由方法的“输出参数”返回给调用者。比如:
像这样的方法一般都会返回BOOL值,用以表示该操作是成功了还是失败了。如果调用者不关心具体的错误信息,直接判断布尔值就行了。否则就检查经由“输出参数”所返回的那个错误对象。
实际上,在使用ARC的时候,编译器会把方法签名中的NSError**转换成NSError * __autoreleasing*,也就是说指针所指向的对象会在方法执行完毕后自动释放。这个对象必须自动释放,因为"doSomething:"方法不能保证其调用者可以把此方法中创建的NSError释放掉,所以必须加入autorelease。
NSError对象里面的domain、code、user information应该按照具体的错误情况填入适当的内容。这样的话调用者就可以根据错误类型分别处理各种错误了。错误范围应该定义成NSString类型的全局常量,错误码则定义成枚举类型更好。例如: