Objective-C消息转发

Objective-C消息转发

static building-静态绑定,即:编译期就能决定运行时所应调用的函数,函数地址实际上是硬编码在指令中。
dynamic building-动态绑定,即:所调用的函数在运行时期才能确定。

void objc_msgSend(id, SEL cmd, …)

发送消息的过程:

  1. 第一步:接收者所属的类方法列表(list of methods)
  2. 第二步:沿着继承体系继续往上找(搜索树,从下到上,从左到右?是吗😶)
  3. 第三步:消息转发(message forwarding)

    这样的步骤去调用一个方法需要调用很多步骤,但是,objc_msgSend函数会 将匹配结果缓存到“快速映射表”(fast map)中,每个类都有这样一个缓存,若是稍后还会执行此方法就直接在缓存表中取出。

消息转发分为两大阶段

  • 第一阶段:先征询接收者所属的类,看其能否动态添加方法,以处理当前这个未知的选择子,即动态方法解析(dynamic method resolution)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void dynamicMethod(id self, SEL _cmd){
NSLog(@"add doing");
}

//动态方法解析
+ (BOOL)resolveInstanceMethod:(SEL)sel{
/*
(lldb) po sel
"doSomething"
*/
if ([NSStringFromSelector(sel) isEqualToString:@"doSomething"]) {//此处可以根据情况自定义条件
//关于"v@:" 参考:https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
class_addMethod(self, sel, (IMP)dynamicMethod, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
  • 第二阶段:此阶段涉及完整的消息转发机制(full forwarding mechanism)
    此阶段分为两个小阶段:
    • forwardingTargetForSelector: 如果返回消息接收的对象则转发结束,如果返回self或者nil,进入下一阶段
    • forwardInvocation:在此是消息转发的最后阶段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//备援接收者
- (id)forwardingTargetForSelector:(SEL)aSelector{
PNMsgForward * forward = [PNMsgForward new];
return forward;
}

/*
完整的消息转发机制
1.必须实现方法签名
2.进行消息转发
注意:如果此处只是进行消息的原样转发,可以直接使用备援接收者实现:
此处的最重要的作用是可以改变消息参数或者改变selector甚至吞并消息(forwardInvocation:中不invoke方法)
*/
- (void)forwardInvocation:(NSInvocation *)anInvocation{
PNMsgForward * forward = [PNMsgForward new];
if ([forward respondsToSelector:anInvocation.selector])
NSLog(@"Nothing");
//下句注释后接收的消息会被吞并,不会报错也没有任何结果
[anInvocation invokeWithTarget:forward];
else
[super forwardInvocation:anInvocation];

}

- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
PNMsgForward * forward = [PNMsgForward new];
NSMethodSignature* signature = [super methodSignatureForSelector:selector];
if (!signature) {
signature = [forward methodSignatureForSelector:selector];
}
return signature;
}

备注:此处实现是在分类实现的;完整转发阶段临时创建了两个对象作为消息接收者或参数

起风了,您的支持将鼓励我继续创作!