天天看点

Objective-C运行时消息派发机制Objective-C运行时消息派发机制

Objective-C运行时消息派发机制

“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”

Objective-C是一门动态语言,鸭子类型是动态语言的重要特性,而id则是Objective-C中最大的一只鸭子。

而Objective-C是如何实现鸭子类型的?这就必须从OC运行是消息派发机制说起。

我们知道当我们的OC对象收到一条看不懂的信息时,会调用doesNotRecognizeSelector并抛出异常,表现在外就是Crash。

然而在抛出异常之前我们有很多机会来教会OC对象来如何阅读这条消息,或者让他看不懂丢给看的懂的对象。

OC对象的方法绑定是被OC Runtime系统推迟到了Runtime,编译器在这里只能给有限的检查和排错。(因为在这里编译器并不能像对静态语言那样做严格的检查。关于动态语言和静态语言的争议,苹果给了他自己的答案,swift是编译时的静态语言,然而运行时却是动态的,这意味着什么自己去思考)。

所以方法的调用在运行时经历了一场非比寻常的旅行。
在Class初始化的时候,method list,method cache也被初始化,method list顾名思义,method cache略过不表。
           

首先向对象发送一条消息

1、对象翻了下Method list小本子,发现自己不知道这条消息在表达什么意思,于是通过isa向上求助super class,如果可以super知道是怎么回事,真是皆大欢喜,如果super也不会→_→,往下走。(此处有Method cache相关过程,略过不表,感兴趣可以翻一翻相关文档)

2、执行resolvInstanceMethod,询问一下是不是现在立马学一下这个SEL怎么响应的。在这里我们可以将备用的IMP插入。返回YES,则在执行一次这个msg目前所对应的IMP。恩恩,我现在可以不想学吗?返回NO,→_→,往下走。

3、开启甩锅模式,执行forwardingTargetForSelector,既然我不会,返回一个可能知道怎么响应这条msg的对象。恩,由返回的这个对象继续执行以上1-N的步骤;人缘好差,居然这个时候没有对象来愿意来接锅,返回nil。→_→,往下走。

4、执行methodSignatureForSelector来获取方法签名。在这里可以找个DuckType来获取指定SEL的方法签名。或者在这里动态的创造一个方法签名并返回;如果无法获取,返回nil,只能遗憾的执行doesNotRecognizeSelector并抛出异常。

5、执行forwardInvocation,这里是最后一次对msg响应的时机,在这里要么对invocation找到正确的响应方式,要么调用super,抛异常(当然也可以不调用)。