本文由快学吧个人写作,以任何方式转载请表明原文出处
一、材料准备
objc4-818.2
对应mac的版本是11.1。可根据自己的系统版本挑选能够进行调试的源码。
二、思路
- 在缓存(包含超类的缓存)和承继链中都找不到办法的imp的时候,
lookUpImpOrForward源码中提到了动态抉择。 - 动态抉择会将sel和imp存入cls的缓存中。所以动态抉择会给sel找一个imp。
- 动态抉择做了什么,才能给sel找imp?
三、动态抉择的源码
- 从
lookUpImpOrForward中能够找到 :
-
resolveMethod_locked是履行了动态协议的回来成果,看源码 :
- 一共4个没看过的函数。全部分开看。
四、实例办法的动态抉择
1. resolveInstanceMethod源码 :
resolveInstanceMethod里边,先查看了调用办法的类是不是承继于NSObject的,假如是能够持续进行,假如不是,直接return。向调用办法的类(cls)经过objc_msgSend发送了一条信息,让cls调用+(BOOL)resolveInstanceMethod办法。(在这个办法中咱们能够给sel做一些操作,比如给sel绑定一个imp。)
再次查找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). 举例代码 :
- 创立一个JDPerson类,类中有一个实例办法:
-(void)zhuanQian;,而且不写它的完结。
- 在
JDPerson.m中完结resolveInstanceMethod办法如下 :
- 履行代码 :
总结 :
中心便是
+ (BOOL)resolveInstanceMethod:(SEL)sel中进行了sel和imp的绑定,将没有办法完结的sel从头绑定一个imp,绑定的办法便是class_addMethod。动态协议完结sel和imp绑定之后(相当于源码中
resolveInstanceMethod中的bool resolved = msg(cls, resolve_sel, sel);)之后,再lookUpImpOrNilTryCache,就会经过缓存或许lookUpImpOrForward找到sel对应的imp。
五、类办法的动态抉择
1. resolveClassMethod
总结 :
resolveClassMethod函数里边先查看元类是不是承继于NSObject的,假如是承继于NSPbject的,那么持续下面的流程,假如不是承继于NSObject的,那么直接return。获取元类的一般类。由于可能在cls这个元类的一般类中完结(重写)了
+resolveClassMethod:办法。经过objc_msgSend,让元类的一般类调用
+resolveClassMethod:办法。(能够在+resolveClassMethod:做一些操作,比如给sel绑定一个imp)。查找cls和cls承继链,获取sel的imp,假如sel有imp,则回来imp,假如没有imp,回来的是nil,imp=nil。
解说一下为什么非要有一个获取元类的一般类的操作 :
由于可能在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)
- 在上面创立过的JDPerson中添加一个类办法 :
+ (void)xueXi;。而且不要完结它。然后让JDPerson调用+ (void)xueXi;。
- 在
JDPerson.m中完结resolveClassMethod办法 :
- 履行代码 :
备注 :
这个比如中的第2步,便是解说了 : 五—>1—>总结—>5,中的为什么非要获取元类的一般类。假如不获取一般类,直接用元类自身,那么我在JDPerson.m中完结的
resolveClassMethod是不会被找到的。由于JDPerson元类只会找NSObject元类和NSObject类,而不会找JDPerson类中的resolveClassMethod办法完结。
(3). 举例代码(不在resolveClassMethod中为sel绑定imp)
假如不在resolveClassMethod办法中绑定类办法的sel和imp,那么就会进入这里 :
看下注释,这个举例便是针对 : 五—>2的举例。
- JDPerson.m中的仅保留如下代码。在JDPerson.h中仍是保留
+ (void)xueXi;。而且不要完结它。然后让JDPerson调用+ (void)xueXi;。
- 创立NSObject的分类,NSObject的分类中完结
resolveInstanceMethod办法 :
- 履行代码 :
4. lookUpImpOrForwardTryCache
- 这是动态解析的最后一步,看源码 :
-
会发现和 : 四—>2中的
lookUpImpOrNilTryCache源码中心是相同的,都是_lookUpImpTryCache。 -
为什么相同的中心源码还要写不同的两个函数?
由于两个函数给_lookUpImpTryCache传的最后一个参数:behavior是不相同的。
behavior的作用是在上一节的
lookUpImpOrForward中操控动态抉择只履行一次的。否则从动态抉择的进口这边再进来,就会一直在lookUpImpOrForward和动态抉择的中间循环。
六、总结
只有承继NSObject的类,才适用本章的内容。
实例办法的动态抉择 :
(1) 会从当时类开始并沿着承继链开始查找并调用
resolveInstanceMethod,resolveInstanceMethod是NSObject默许完结的,默许回来值是NO。(2) 咱们能够在当时类或许当时类的超类(一直到NSObject的分类)中重写
resolveInstanceMethod办法。能够在里边自己写一些逻辑,用于给没有imp的sel从头绑定一个imp,避免呈现因找不到办法完结而呈现的溃散。
- 类办法的动态抉择 :
分为两种办法 :
(1) 一种是
resolveClassMethod。会从当时类开始并沿着承继链开始查找并调用resolveClassMethod,resolveClassMethod也是NSObject默许完结的,默许回来值是NO。咱们能够在当时类或许当时类的超类(一直到NSObject的分类)中重写
resolveClassMethod办法。能够在里边自己写一些逻辑,用于给没有imp的sel从头绑定一个imp,避免呈现因找不到办法完结而呈现的溃散。(2) 一种是
resolveInstanceMethod。针对类办法的动态抉择,只有在进行过resolveClassMethod,可是类办法的sel依然找不到imp的情况下,才会被调用。
resolveInstanceMethod为类办法的sel绑定imp只能在NSObject的分类中重写绑定逻辑,才有用。




























