消息机制
- OC中的方法调用其实都是转成了objc_msgSend函数的调用,给receiver(方法调用者)发送了一条消息(selector方法名)
objc_msgSend执行流程
objc_msgSend的执行流程可以分为3大阶段
- 消息发送
- 动态方法解析
- 消息转发
objc_msgSend执行流程01-消息发送
- 执行流程如下图
- receiver通过isa指针找到receiverClass
- receiverClass通过superclass指针找到superClass
- 如果是从class_rw_t中查找方法
- 已经排序的,二分查找
- 没有排序的,遍历查找
objc_msgSend执行流程02-动态方法解析
- 如果在消息发送阶段没有找到方法,就会来到动态方法解析
- 开发者可以实现以下方法,来动态添加方法实现
- +resolveInstanceMethod:
- +resolveClassMethod:
- 动态解析过后,会重新走“消息发送”的流程 “从receiverClass的cache中查找方法”这一步开始执行
动态添加方法
- 代码如下
void other (id self, SEL _cmd) {
NSLog(@"%@ - %s",self,_cmd);
}
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == @selector(test)) {
class_addMethod(self, sel, (IMP)other, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
objc_msgSend的执行流程03-消息转发
- 开发者可以在forwardInvocation:方法中自定义任何逻辑
- 以上方法都有对象方法、类方法2个版本(前面可以是加号+,也可以是减号-)
- 具体实现如下,如果实现了forwardingTargetForSelector,并且有返回值。
- (id)forwardingTargetForSelector:(SEL)aSelector
{
if (aSelector == @selector(test)) {
return [[Cat alloc] init];
}
}
// 如果实现了上面方法,返回值为Cat实例对象,就会执行Cat的-test方法。
// 如果实现了上面方法,返回值为Cat类对象,就会执行Cat的+test方法。
- 如果没有实现forwardingTargetForSelector方法,就会找methodSignatureForSelector方法
// 方法签名:返回值类型、参数类型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
if (aSelector == @selector(test)) {
NSMethodSignature *sign = [NSMethodSignature signatureWithObjCTypes:"v@:"];
return sign;
}
// NSMethodSignature *sign1 = [NSMethodSignature instanceMethodSignatureForSelector:@selector(other)];
return nil;
}
- 再执行forwardInvocation方法
// NSInvocation封装了一个方法调用,包括:方法调用者、方法名、方法参数
// anInvocation.target 方法调用者
// anInvocation.selector 方法名
// [anInvocation getArgument:NULL atIndex:0]
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
// 表示执行cat的test方法
// anInvocation.target = [[Cat alloc] init];
// [anInvocation invoke];
// 另一种写法
// [anInvocation invokeWithTarget:[[Cat alloc] init]];
// 直接打印提示方法找不到
// NSLog(@"%@-%@方法找不到",anInvocation.target,NSStringFromSelector(anInvocation.selector));
}
super的本质
- 新建一个Student类继承至Person,Student实现一个方法调用父类的方法。
Student *s = [[Student alloc] init];
[s test1];
@interface Student : Person
- (void)test1;
@end
- (void)test1
{
[super test];
}
- 通过
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc命令得到c++代码
static void _I_Student_test1(Student * self, SEL _cmd) {
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Student"))}, sel_registerName("test"));
//去掉一些强转
objc_msgSendSuper((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Student"))}, sel_registerName("test"));
}
-
需要注意的是,通过断点看汇编执行流程,发现执行的并不是objc_msgSendSuper,而是objc_msgSendSuper2
-
我们通过clang命令得到的c++代码只能作为参考,大部分都是正确的。
-
通过查看objc4源码发现objc_msgSendSuper2传入的2个参数应该是
- struct objc_super2 { id receiver; //receiver是消息接收者 Class current_class; //current_class是receiver的Class对象 }结构体
- SEL
-
super调用的本质就是直接从父类的类对象或元类对象去找方法的实现,消息接收者还是自身。