本文由快学吧个人写作,以任何方式转载请表明原文出处

一、材料准备

objc4-818.2

对应mac的版本是11.1。可根据自己的系统版本挑选能够进行调试的源码。

二、思路

  1. 在缓存(包含超类的缓存)和承继链中都找不到办法的imp的时候,lookUpImpOrForward源码中提到了动态抉择。
  2. 动态抉择会将sel和imp存入cls的缓存中。所以动态抉择会给sel找一个imp。
  3. 动态抉择做了什么,才能给sel找imp?

三、动态抉择的源码

  1. lookUpImpOrForward中能够找到 :

十四、动态方法决议

  1. resolveMethod_locked是履行了动态协议的回来成果,看源码 :

十四、动态方法决议

  1. 一共4个没看过的函数。全部分开看。

四、实例办法的动态抉择

十四、动态方法决议

1. resolveInstanceMethod源码 :

十四、动态方法决议

十四、动态方法决议
总结 :

  1. resolveInstanceMethod里边,先查看了调用办法的类是不是承继于NSObject的,假如是能够持续进行,假如不是,直接return。

  2. 向调用办法的类(cls)经过objc_msgSend发送了一条信息,让cls调用+(BOOL)resolveInstanceMethod办法。(在这个办法中咱们能够给sel做一些操作,比如给sel绑定一个imp。)

  3. 再次查找sel有没有在cls或许cls承继链上完结。假如有,拿到imp。假如没有,imp = nil。

2. lookUpImpOrNilTryCache源码 :

十四、动态方法决议

_lookUpImpTryCache源码 :

十四、动态方法决议

总结 :

查找缓存中的imp。

(1). 假如自己的缓存中有imp,直接获取,而且直接去done:中直接回来imp,不再履行下面的判别。

(2). 假如自己缓存中没有imp,查找同享缓存,获取imp。

(3). 假如同享缓存没有imp,回来lookUpImpOrForward流程的回来成果。

3. 举例

看到这里仍是不知道实例办法的动态抉择有什么用,或许说上面的源码都说了什么。其中的要点便是提到了一个办法 : +resolveInstanceMethod办法。

(1). 找一下resolveInstanceMethod办法 :

十四、动态方法决议

这是一个NSObject类的办法。也便是说,只要是承继于NSObject的类,都能够使用。

(2). 举例代码 :

  1. 创立一个JDPerson类,类中有一个实例办法:-(void)zhuanQian;,而且不写它的完结。

十四、动态方法决议

十四、动态方法决议

  1. JDPerson.m中完结resolveInstanceMethod办法如下 :

十四、动态方法决议

  1. 履行代码 :

十四、动态方法决议

总结 :

  1. 中心便是+ (BOOL)resolveInstanceMethod:(SEL)sel中进行了sel和imp的绑定,将没有办法完结的sel从头绑定一个imp,绑定的办法便是class_addMethod

  2. 动态协议完结sel和imp绑定之后(相当于源码中resolveInstanceMethod中的bool resolved = msg(cls, resolve_sel, sel);)之后,再lookUpImpOrNilTryCache,就会经过缓存或许lookUpImpOrForward找到sel对应的imp。

五、类办法的动态抉择

十四、动态方法决议

1. resolveClassMethod

十四、动态方法决议

十四、动态方法决议

总结 :

  1. resolveClassMethod函数里边先查看元类是不是承继于NSObject的,假如是承继于NSPbject的,那么持续下面的流程,假如不是承继于NSObject的,那么直接return。

  2. 获取元类的一般类。由于可能在cls这个元类的一般类中完结(重写)了+resolveClassMethod:办法。

  3. 经过objc_msgSend,让元类的一般类调用+resolveClassMethod:办法。(能够在+resolveClassMethod:做一些操作,比如给sel绑定一个imp)。

  4. 查找cls和cls承继链,获取sel的imp,假如sel有imp,则回来imp,假如没有imp,回来的是nil,imp=nil。

  5. 解说一下为什么非要有一个获取元类的一般类的操作 :

    由于可能在cls这个元类的一般类中完结(重写)了+resolveClassMethod:办法。

    举个比如 : 假设A类的元类叫A元类,而且是A类中重写了+resolveClassMethod:办法,而不是在NSObject或许NSObject的分类中重写的+resolveClassMethod:办法。假如objc_msgSend的消息接收者是A元类,让A元类去调用+resolveClassMethod:办法,那么A元类只会在 : A元类自身中、A元类的父类中和NSObject中去查找+resolveClassMethod:办法的完结,而不会找到A类的+resolveClassMethod:办法完结。终究找到的只会是NSObject中的+resolveClassMethod:办法完结,那是个默许的办法完结,里边直接回来NO,不会做其他的操作。也就等于,咱们在A类中重写的+resolveClassMethod:办法没有被找到,也没有被调用。

2. 类的动态抉择为什么会多一个步骤:判别,是否要调用resolveInstanceMethod

十四、动态方法决议

先看我的注释。要点在resolveInstanceMethod中的bool resolved = msg(cls, resolve_sel, sel);这一行。

由于现在cls是元类,元类中自身是没有+resolveInstanceMethod:办法的,发消息给元类,让元类调用这个办法,必定调用的不是元类自身的这个办法,那么调用的就应该是父类的。

元类的父类是根元类,也便是NSObject元类,所以终究这个bool resolved = msg(cls, resolve_sel, sel);代码,是元类cls调用到了NSObject中的+resolveInstanceMethod:办法。

假如想给sel绑定一个imp,+resolveClassMethod()没有做到,会进入到这个if判别,那么就应该在NSObject或许NSObject的分类中重写+resolveInstanceMethod:办法,在重写中完结给sel绑定一个imp。

3. 举例

如何用resolveClassMethod处理一些问题。举个简略的比如。

(1). 找一下resolveClassMethod办法 :

十四、动态方法决议

(2). 举例代码(在resolveClassMethod中为sel绑定imp)

  1. 在上面创立过的JDPerson中添加一个类办法 : + (void)xueXi;。而且不要完结它。然后让JDPerson调用+ (void)xueXi;

十四、动态方法决议

十四、动态方法决议

  1. JDPerson.m中完结resolveClassMethod办法 :

十四、动态方法决议

  1. 履行代码 :

十四、动态方法决议

备注 :

这个比如中的第2步,便是解说了 : 五—>1—>总结—>5,中的为什么非要获取元类的一般类。假如不获取一般类,直接用元类自身,那么我在JDPerson.m中完结的resolveClassMethod是不会被找到的。由于JDPerson元类只会找NSObject元类和NSObject类,而不会找JDPerson类中的resolveClassMethod办法完结。

(3). 举例代码(不在resolveClassMethod中为sel绑定imp)

假如不在resolveClassMethod办法中绑定类办法的sel和imp,那么就会进入这里 :

十四、动态方法决议

看下注释,这个举例便是针对 : 五—>2的举例。

  1. JDPerson.m中的仅保留如下代码。在JDPerson.h中仍是保留+ (void)xueXi;。而且不要完结它。然后让JDPerson调用+ (void)xueXi;

十四、动态方法决议

十四、动态方法决议

  1. 创立NSObject的分类,NSObject的分类中完结resolveInstanceMethod办法 :

十四、动态方法决议

  1. 履行代码 :

十四、动态方法决议

4. lookUpImpOrForwardTryCache

  1. 这是动态解析的最后一步,看源码 :

十四、动态方法决议

十四、动态方法决议

  1. 会发现和 : 四—>2中的lookUpImpOrNilTryCache源码中心是相同的,都是_lookUpImpTryCache

  2. 为什么相同的中心源码还要写不同的两个函数?

由于两个函数给_lookUpImpTryCache传的最后一个参数:behavior是不相同的。

behavior的作用是在上一节的lookUpImpOrForward中操控动态抉择只履行一次的。否则从动态抉择的进口这边再进来,就会一直在lookUpImpOrForward和动态抉择的中间循环。

六、总结

  1. 只有承继NSObject的类,才适用本章的内容。

  2. 实例办法的动态抉择 :

(1) 会从当时类开始并沿着承继链开始查找并调用resolveInstanceMethodresolveInstanceMethod是NSObject默许完结的,默许回来值是NO。

(2) 咱们能够在当时类或许当时类的超类(一直到NSObject的分类)中重写resolveInstanceMethod办法。能够在里边自己写一些逻辑,用于给没有imp的sel从头绑定一个imp,避免呈现因找不到办法完结而呈现的溃散。

  1. 类办法的动态抉择 :

分为两种办法 :

(1) 一种是resolveClassMethod。会从当时类开始并沿着承继链开始查找并调用resolveClassMethodresolveClassMethod也是NSObject默许完结的,默许回来值是NO。

咱们能够在当时类或许当时类的超类(一直到NSObject的分类)中重写resolveClassMethod办法。能够在里边自己写一些逻辑,用于给没有imp的sel从头绑定一个imp,避免呈现因找不到办法完结而呈现的溃散。

(2) 一种是resolveInstanceMethod。针对类办法的动态抉择,只有在进行过resolveClassMethod,可是类办法的sel依然找不到imp的情况下,才会被调用。

resolveInstanceMethod为类办法的sel绑定imp只能在NSObject的分类中重写绑定逻辑,才有用。