objc_msgSend本质

150 阅读2分钟

在开发中离不开方法调用,比如下面最简单的调用

    Person *p = [[Person alloc] init];
    [p nxyTest:10];  这句代码的底层实现是 objc_msgSend(p, @selector(test:));

方法调用的本质就是发消息 下面就来看看 objc_msgSend 是如何一步步实现的

  1. 消息发送

    消息发送图
    如上面流程图所示, 依次去方法缓存(cache), 方法列表(class_rw_t),父类对应的缓存和方法列表去找, 如果有就去调用,如果没有找到,就执行下一个阶段"动态方法解析"

  2. 动态方法解析

    动态方法解析图

@interface Person : NSObject
-(void)test;
+(void)replaceClassTest;
@end
@implementation Person
//动态添加对象方法
+(BOOL)resolveInstanceMethod:(SEL)sel
{
    if (sel == @selector(test)) {
        //获取其他方法
        Method method = class_getInstanceMethod(self, @selector(other));
        //动态添加test方法的实现
        class_addMethod(self, sel,
                        method_getImplementation(method),
                        method_getTypeEncoding(method));
        //返回YES表示有动态添加方法
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}
-(void)other
{
    NSLog(@"%s",__func__);
}
@end


//动态添加类方法
+(BOOL)resolveClassMethod:(SEL)sel{
    if (sel == @selector(classTest)) {
         Method method = class_getClassMethod(self, @selector(replaceClassTest));
         //动态添加方法的实现
         class_addMethod(object_getClass(self), sel, method_getImplementation(method), method_getTypeEncoding(method));
         return YES;
     }
     return [super respondsToSelector:sel];
}
+(void)replaceClassTest{
     NSLog(@"++++++++++++++replaceClassTest");
}

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    Person *p = [[Person alloc] init];
    [p test];
    [Person replaceClassTest];
}
@end

Person类中只有test/replaceClassTest方法的声明,而没有实现,在外部调用test/replaceClassTest方法的时候,在第一阶段消息发送没有找到对应的方法去调用,所以来到了第二阶段动态解析,可以在这里动态的添加方法

3.消息转发

消息转发图

//对象方法
@implementation Person
//消息转发
-(id)forwardingTargetForSelector:(SEL)aSelector
{
    if (aSelector == @selector(test)) {
      /*如果这里返回了一个sutdent对象, 
       那么就会调用objc_msgSend(student, aSelector), 
       如果这里返回nil,则会调用methodSignatureForSelector*/
        return [[Student alloc] init];
    }
    return [super forwardingTargetForSelector:aSelector];
}
//如果返回了NSMethodSignature对象, 接下来会调用forwardInvocation方法, 如果返回nil,报方法找不到的错误
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    if (aSelector == @selector(test:)) {
        return [NSMethodSignature signatureWithObjCTypes:"v@"];
//        return [[[Student alloc] init] methodSignatureForSelector:aSelector];

       // Method method = class_getInstanceMethod([Student class], aSelector);
       // return  [NSMethodSignature signatureWithObjCTypes:method_getTypeEncoding(method)];
    }
    return [super methodSignatureForSelector:aSelector];
}
/**
 NSInvocation 是封装了一个方法调用,  包括 : 方法调用者 方法名 方法参数
 anInvocation.target 方法调用者
 anInvocation.selector  方法名
  anInvocation getArgument:<#(nonnull void *)#> atIndex:<#(NSInteger)#>   方法参数
 */
-(void)forwardInvocation:(NSInvocation *)anInvocation
{
   //获取方法的参数
    int argument;
//    [anInvocation getArgument:&argument atIndex:2];
//    NSLog(@"%d",argument);
      [anInvocation invokeWithTarget:[[Teacher alloc]init]];
}
@end
//类方法
+(id)forwardingTargetForSelector:(SEL)aSelector {
    if (aSelector == @selector(classTest)) {
        return [Student class];
    }
    return [super forwardingTargetForSelector:aSelector];
}

+(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if (aSelector == @selector(classTest)) {
        Method method = class_getInstanceMethod(object_getClass([Student class]), aSelector);
        return [NSMethodSignature signatureWithObjCTypes:method_getTypeEncoding(method)];
    }
    return [super methodSignatureForSelector:aSelector];
}
+(void)forwardInvocation:(NSInvocation *)anInvocation {
    [anInvocation invokeWithTarget:[Student class]];
}