OC-消息机制

246 阅读3分钟

消息机制

  • 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调用的本质就是直接从父类的类对象或元类对象去找方法的实现,消息接收者还是自身。