0、方法调用原理
0.1、以[reciver method]为例,实际上是objc_msgSend(reciver, @selector(method), …argments);
0.2、OC方法调用转换为objc_msgSend函数调用,方法调用者、方法名为该函数的前2个参数;
0.3、OC对象本质为结构体,参考:剖析Objective-C对象本质结构MJClassInfo
1、消息发送(查找方法)
1.1、如果方法调用者为nil,直接返回(不报错);
2.2、方法调用者通过isa指针查找方法接收者,方法接收者查找对象中的cache,如果没有则查找对象中方法列表class_rw_t;
2.3、如果找到则调用并缓存在cache中,否则通过superclass在父类中的cache,class_rw_t中查找;
2.4、如果找到则调用并缓存在cache中,否则再通过superclass的superclass查找,直到superclass为空;
2.5、如果在整个继承体系中都没有找到目标方法,调用方法接收者的动态解析方法(如果解析过则进入方法转发);
2、动态解析
2.1、两个相关方法:resoloveInstanceMethod:、resoloveClassMethod:前者为实例对象动态解析,后者为类\元类对象动态解析;
2.2、进入动态解析方法,需要完成的工作是给方法接收者添加方法实现,如通过class_addMetho()函数实现;
1 | class_addMethod(Class cls, SEL name, IMP imp, const char *types) |
2.2.1、第一参数为方法接收者:实例方法调用则cls为类对象,类方法调用则为元类对象;
2.2.2、第二个参数为方法名,可直接使用传进来的形参;
2.2.3、第三个参数与第四个参数与方法对象Method相关,可通过method_getImplementation()、method_getTypeEncoding()获取,而Method对象可通过class_getClassMethod(Class cls, SEL sel)获取;
2.3、添加方法实现后,原来调用sel方法动作变成调用method方法,解析后的方法不关心方法性质,即对象方法或类方法都可以;
2.4、解析完成后,再次执行方法调用各步骤,如果查找到了动态方法则执行,如果还是没有找到,不再进行动态解析,进入方法转发阶段;
3、方法转发
3.1、相关方法
1 2 3 4 5 6 7 | - (id)forwardingTargetForSelector:(SEL)aSelector; + (id)forwardingTargetForSelector:(SEL)aSelector; - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector; + (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector; - (void)forwardInvocation:(NSInvocation *)anInvocation; + (void)forwardInvocation:(NSInvocation *)anInvocation; |
3.1、消息转发首先调用forwardingTargetForSelector:方法,该方法要求返回一个实现了原方法的对象;
3.1.1、如果返回对象不为空,则调用对象的aSelector方法,这也是一个方法调用,同样符合当前所有的机制,即从消息发送开始—>动态解析—->方法转发这一系列动作,可以理解为是一次递归;
3.1.2、如果该方法没有实现,或返回为空,则调用方法签名
3.2、方法签名methodSignatureForSelector:该方法只需要返回一个有效签名即可,接下来会调用forwardInvocation:
3.2.1、如果返回为空或不是有效的NSMethodSignature对象,则调用doesNotRecognizeSelector方法抛出异常;
3.2.2、如果返回一个有效的NSMethodSignature,forwardInvocation:没有实现同样调用doesNotRecognizeSelector方法抛出异常;
3.3、forwardInvocation:会根据方法签名给出NSInvocation对象,只要实现了该方法即会执行该方法内的代码为方法最终归宿;
4、如果以上所有操作什么都没做,则直接调用doesNotRecognizeSelector方法抛出异常;
5、总结过程中的几个方法
1 2 3 4 5 6 7 8 9 10 11 | // 动态解析 + (BOOL)resolveInstanceMethod:(SEL)sel; + (BOOL)resolveClassMethod:(SEL)sel; // 方法转发 - (id)forwardingSignatureMethodForSelector:(SEL)aSelector; + (id)forwardingSignatureMethodForSelector:(SEL)aSelector; // 方法签名 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector; + (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector; - (void)forwardInvocation:(NSInvocation *)anInvocation; + (void)forwardInvocation:(NSInvocation *)anInvocation; |
Pingback: 定时器NSTimer、CADisplayLink及代理NSProxy的基本使用 | 一天到晚游泳的余
– (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
– (void)forwardInvocation:(NSInvocation *)anInvocation
输出结果:
2018-11-21 10:49:37.669124+0800 runtime[2166:353273] -[ViewController forwardInvocation:]
– (id)forwardingTargetForSelector:(SEL)aSelector 实现:
+ (BOOL)resolveInstanceMethod:(SEL)sel 示例: