消息传递和消息转发
- objc_msgSend执行流程
- 消息发送
- 动态方法解析
- 消息转发
- super
- 面试题
objc_msgSend执行流程
OC中的方法调用,其实都是转化为objc_msgSend函数调用,objc_msgSend的执行流程可以分为3大阶段
-
- 消息发送
-
- 动态方法解析
-
- 消息转发
消息发送
消息发送流程是我们平时最经常使用的流程,其他的像动态方法解析和消息转发其实是补救措施。具体流程如下
1、首先判断消息接受者receiver是否为nil,如果为nil直接退出消息发送
2、如果存在消息接受者receiverClass,首先在消息接受者receiverClass的cache中查找方法,如果找到方法,直接调用。如果找不到,往下进行
3、没有在消息接受者receiverClass的cache中找到方法,则从receiverClass的class_rw_t中查找方法,如果找到方法,执行方法,并把该方法缓存到receiverClass的cache中;如果没有找到,往下进行
4、没有在receiverClass中找到方法,则通过superClass指针找到superClass,也是现在缓存中查找,如果找到,执行方法,并把该方法缓存到receiverClass的cache中;如果没有找到,往下进行
5、没有在消息接受者superClass的cache中找到方法,则从superClass的class_rw_t中查找方法,如果找到方法,执行方法,并把该方法缓存到receiverClass的cache中;如果没有找到,重复4、5步骤。如果找不到了superClass了,往下进行
6、如果在最底层的superClass也找不到该方法,则要转到动态方法解析
动态方法解析
(把这两个方法名记住:+resolveInstanceMethod: +resolveClassMethod: )
- 开发者可以实现以下方法,来动态添加方法实现
- +resolveInstanceMethod:
- +resolveClassMethod:
- 动态解析过后,会重新走“消息发送” 的流程,从receiverClass的cache中查找方法这一步开始执行
+ (BOOL)resolveInstanceMethod:(SEL)sel { //sel代表无法响应的方法名
if (sel == @selector(test)) {
//获取其他方法
Method method = class_getInstanceMethod([self class], @selector(other)); //这是一个runtime函数,需要包含头文件#import <objc/runtime.h>,
//参数一:(class)sel是未能响应的方法的类,就传[self class]
//参数二(SEL)name是要查找的方法名(就是自己动态添加的方法的方法名),传 @selector(方法名)
//动态添加test的方法
class_addMethod([self class], sel, method_getImplementation(method), method_getTypeEncoding(method));//这也是一个runtime函数,用于动态的向一个类添加一个新的方法。
//参数一:(Class)cls是要添加方法的类,就传[self class]
//参数二:(SEL)name表示未能相应的方法名,就传sel
//参数三:(IMP)imp是方法的实现函数指针, 就传method_getImplementation(method)
//参数四:(const char*)types是方法的类型编码字符串,就传method_getTypeEncoding(method))
}
return [super resolveInstanceMethod:sel];
} //系统默认返回NO
可以看出,这个方法的实现大部分内容都是固定的。完全套模版。
如果这个方法没有实现,默认返回NO,并查看有没有实现forwardingTargetForSelector:方法
消息转发
如果一个方法在消息发送阶段没有找到相关方法,也没有进行动态方法解析,这个时候就会走到消息转发阶段了。
(==再记住下面这三个方法:forwardingTargetForSelector , methodSignatureForSelectot , forwardInvocation: ,doesNotRecognizeSelector: ==)
- 调用forwardingTargetForSelector,返回值不为nil时,会调用objc_msgSend(返回值, SEL)
- 调用methodSignatureForSelector,返回值不为nil,调用forwardInvocation:方法;返回值为nil时,调用doesNotRecognizeSelector:方法
- 开发者可以在forwardInvocation:方法中自定义任何逻辑
- 以上方法都有对象方法、类方法2个版本(前面可以是加号+,也可以是减号-)
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(test)) {
return [[Student alloc] init];
}
return nil;
} //系统默认返回nil
//调用forwardingTargetForSelector,返回值不为nil时,会调用objc_msgSend(返回值, SEL),结果就是调用了objc_msgSend(Student,test)
这里我的理解就是,把这个方法换一个对象来调用,调用成功的前提是,该对象的类实现了此方法名的方法
forwardingTargetForSelector如果没有实现,默认返回nil。进入后续的方法。
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(test)) {
//这三种实现方法都可以
return [[[Student alloc] init] methodSignatureForSelector:aSelector];
//return [[Student class] instanceMethodSignatureForSelector:aSelector];
//return [NSMethodSignature signatureWithObjCTypes:"i@:i"];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
if ([anInvocation selector] == @selector(test)) {
} else {
}
NSLog(@" 直接在这里写方法实现,这里我想干嘛就干嘛");
}
forwardingTargetForSelector:返回nil,才会进入这一步。
如果methodSignatureForSelector: 获取方法签名成功,则调用forwardInvocation:来转发消息,如果失败,就是经典的doesNotRecognizeSelector:报错
forwardInvocation:方法的使用非常灵活
anInvocation 参数是一个 NSInvocation 对象,它封装了一个方法调用的所有信息,包括:
- 目标对象 (即将被调用方法的对象)
- 方法选择器 (即将被调用的方法)
- 方法参数 (调用方法时传递的参数)
- 返回值 (方法的返回值)
例如,你可以使用以下方法获取方法选择器和参数:
SEL theSelector = [anInvocation selector];
const char *methodType = [anInvocation methodSignature].methodReturnType;
你还可以设置新的目标对象和参数,然后调用 invokeWithTarget: 方法将消息转发给另一个对象:
[anInvocation setTarget:anotherObject];
[anInvocation setArgument:&someArgument atIndex:2];
[anInvocation invoke];
通过这种方式,你可以将收到的消息转发给其他对象来处理。这在实现动态消息转发的时候非常有用。
另外,anInvocation 对象还提供了一些其他有用的方法,比如 getReturnValue: 和 getArgument:atIndex: 等,可以帮助你获取和设置方法的返回值和参数。
super
待补充
面试题
- 什么是消息转发?它是如何工作的?简要回答。
答:消息转发是Objective-C处理未识别消息的机制。
当对象无法响应某消息时,会进入消息转发流程:
1. 动态方法解析:通过实现+resolveInstanceMethod:或+resolveClassMethod:动态添加方法。
2. 备用接收者:通过-forwardingTargetForSelector:指定另一个对象处理消息。
3. 完整消息转发:通过-methodSignatureForSelector:和-forwardInvocation:完整转发消息。
- 什么是Objective-C中的消息传递?与C++中 的方法调用有什么区别?底层实现过程?
答:
- 在Objective-C中,消息传递是对象间通信的方式。发送消息的语法为[object message],底层实现是将消息发送给对象的isa指针指向的类,在方法列表中查找对应的方法实现。
- 与C++中的方法调用区别:
Objective-C通过运行时系统实现消息传递,方法绑定在运行时完成。
C++通过编译时绑定(静态绑定),方法在编译时确定。 - 底层实现过程:
消息传递底层实现通过objc_msgSend函数:-
- 查找方法:根据对象的isa指针,找到类的method_list,查找对应的SEL(选择器)。
-
- 执行方法:找到方法实现后,执行该方法。如果找不到,进入消息转发机制。
-