本文由快学吧个人写作,以任何方式转载请表明原文出处
一、材料准备
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的分类中重写绑定逻辑,才有用。