拯救即将崩溃代码之Objective-C消息转发

Posted w3c应用

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了拯救即将崩溃代码之Objective-C消息转发相关的知识,希望对你有一定的参考价值。

日常开发中,有时调用对象的某个方法,会出现异常,如下:


*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[ViewController doSomething:]: unrecognized selector sent to instance 0x7fcd815bc580'


意思是在类ViewController中调用doSomething方法,但是没有找到该方法,程序异常,然后崩溃。




对于这种情况,有没有什么补救措施防止程序崩溃呢?


答案是有,方式大致有几种:


1、用try...catch来包住可能会出问题的代码;


2、调方doSomething方面前先用respondsToSelector来判断有无指定的方法,有再调用;


3、NSSetUncaughtExceptionHandler机制来控制处理;


4、使用Objective-C Runtime的消息转发机制来处理。


  处理方式 说明

1 用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#>

    }

2 调方doSomething方面前先用respondsToSelector来判断有无指定的方法,有再调用; if ([类的实例对象 respondsToSelector:@selector(doSomething:)]) {

            [类的实例对象 doSomething:@"xxx"];

}

3 NSSetUncaughtExceptionHandler机制来控制处理; NSSetUncaughtExceptionHandler(&ocExceptionHandle);

void ocExceptionHandle(NSException *exception)

{

    //...

}

4 使用Objective-C的消息转发机制来处理。 1.+ resolveInstanceMethod:(SEL)sel // 对应实例方法

        或者 + resolveClassMethod:(SEL)sel // 对应类方法

2.- (id)forwardingTargetForSelector:(SEL)aSelector

3.- (void)forwardInvocation:(NSInvocation *)anInvocation

说明下:


对于方法1,try...catch可以抓住上述NSInvalidArgumentException异常。try...catch适用于未来不可知的情况,但是对于知道可能会有问题的地方,还是需要编码时加强健壮性,就像上面的方法2,respondsToSelector就是用来判断是否存在指定的方法,如果存在再调用就不会发生错误。


对于方法3,适用于我们没有写try...catch的代码,如果发生异常,就会被我们指定的异常Handler抓住,这个时候就可以做相关处理,比如将异常信息记日志、发送服务器或者忽略等操作。


而方法4,消息转发(Message Forward),是Objective-C语言级别的特性,即Runtime的特性,也是Objective-C动态语言的特色之一。Objective-C Runtime会提供3次挽救的机会(准确的说是1次动态方法解析+2次消息转发;其中实例方法救3次,类方法救1次):


  正式名称 通俗理解 实例方法 类方法

1 Dynamic Method Resolution

(动态方法解析) 第一次挽救:Runtime说给我一个可以用的替代方法。

2 Fast Forwarding

(快速消息转发,指定备用接受者) 第二次挽救:Runtime说告诉我谁可以搞定这个事。  

3 Normal Message Forward

(消息转发) 第三次挽救:Runtime说我把这个烂事包装成NSInvocation给你,你看着办吧。  



补充一点:


1、Objective-C实例对象调用一个方法,会首先在本类方法列表查找,如果没有,会在父类再查找,直到根类NSObject,在任何一层找到方法,则执行,如果到了最后根类NSObject还没有找到,才会触发Objective-C Runtime的消息转发机制。


2、Objective-C类对象调用一个方法,会首先在本类的元类方法列表中查找,如果没有,会在元类的父类再查找,直到根类NSObject的元类,在任何一层找到方法,则执行,如果到了最后根类NSObject元类还没有找到,才会触发Objective-C Runtime的消息转发机制。


(关于Objective-C的类、元类相关知识可以参考我的另一篇文章:Objective-C类之关系)




例子:


有个类ClassA,现在打算调用一个方法doSomething:(NSString *)val,如果这个doSomething方法不存在就会报异常然后程序崩溃;如果在消息转发的阶段我们做了处理就会挽救灾难于崩溃之前。

--------------------- 


以上是关于拯救即将崩溃代码之Objective-C消息转发的主要内容,如果未能解决你的问题,请参考以下文章

iOS之深入解析消息转发objc_msgSend的应用场景

轻松学习之 Objective-C消息转发 | 干货

iOS理解Objective-C中消息转发机制附Demo

iOS面试粮食Runtime—消息传递和转发机制Method Swizzling

iOS面试粮食Runtime—消息传递和转发机制Method Swizzling

iOS消息转发