美文网首页
底层原理:Runtime之Objc_msgSend用法

底层原理:Runtime之Objc_msgSend用法

作者: 飘摇的水草 | 来源:发表于2022-05-02 12:41 被阅读0次

objc_msgSend用法

  • OC中的方法调用其实都是转成了objc_msgSend函数的调用,给receiver(方法调用者)发送了一条消息(selector方法名)
Person *person = [[Person alloc] init];
[person personTest];
// objc_msgSend(person, @selector(personTest));
// 消息接收者(receiver):person
// 消息名称:personTest
        
[Person initialize];
// objc_msgSend([MJPerson class], @selector(initialize));
// 消息接收者(receiver):[MJPerson class]
// 消息名称:initialize
  • 当用对象调用方法时,如以下:[person test]时,其实是被转化成了C语言的调用objc_msgSend(person,sel_registerName("test")),其中sel_registerName("test") == @selector(test),如果是调用类方法则为:objc_msgSend([Person class],@selector(initialize)),这就是OC的方法调用,也叫消息机制,给方法调用者发送消息。
  • OC中方法调用,其实都是转换为objc_msgSend函数的调用
  • objc_msgSend的执行流程可以分为3大阶段:
    • 消息发送
    • 动态方法解析
    • 消息转发

objc_msgSend流程解析

  1. 在消息发送阶段会去尝试找到这个方法来进行调用,如果在当前类的方法缓存中查找,如果找不到则去它的 rw_t 缓存中查找,如果当前类没找到,则去父类中找,如果找到则直接调用,就不会进行动态方法解析,否则会进行动态方法解析(这两个方法中的其中一个:resolveInstanceMethodresolveClassMethod),允许开发者动态去创建一个新的方法,如果动态方法解析没有处理,则进入消息转发阶段,有可能会转发给另外一个对象去调用,最终完成方法调用,如果最终这三个步骤都没有找到则会报一个:unrecognized selector sent to instance

objc_msgSend执行流程01-消息发送:

消息发送流程总结.jpeg

如果在自己的方法缓存和父类的方法缓存里没有找到方法,则就会进入动态方法解析

2. 动态方法解析

  • 如果不是元类对象则调用:resolveInstanceMethod,如果是元类对象则调用:resolveClassMethod,如果不实现这两个方法表示动态方法解析不做任何处理
    动态解析流程图.png

完整的代码实现如下:

@implementation Person

struct method_t
{
    SEL sel;
    char *types;
    IMP imp;
};

+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    if (sel == @selector(test))
    {
        //第一种做法
        //获取其他方法
//        struct method_t *method = (struct method_t *)class_getInstanceMethod(self, @selector(other));
//        //动态添加test方法的实现
//        class_addMethod(self, sel, method -> imp, method->types);

        //第二种做法
        Method otherMethod = class_getInstanceMethod(self, @selector(other));
        //动态添加方法的实现,IMP表示方法的实现
        class_addMethod(self, sel, method_getImplementation(otherMethod), method_getTypeEncoding(otherMethod));
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

- (void)other
{
    NSLog(@"%s",__func__);
}

上面的代码表示如果没有找到 test 方法则就会调用 other 方法

3. 消息转发

  • 将消息转发给别人,如果方法没有在自己类和父类里找到,同时也没有做动态消息转发的话,就会来到消息转发。
  • forwadingTargetForSelector:(SEL)aSelectoraSelector转发给有能力处理的类,相当于新的对象调用这个aSelector方法。

代码如下:

@implementation Person
 - (id)forwardingTargetForSelector:(SEL)aSelector
{
    if (aSelector == @selector(test))
    {
        return [[Cat alloc]init];
    }
    return [super forwardingTargetForSelector:aSelector];
}
@end

上面的代码Person类不处理test方法而将其转发给Cat类处理,Cat类里的代码如下:

@implementation Cat
- (void)test
{
    NSLog(@"%s",__func__);
}
@end

此时当Person对象调用test方法时,最终会调用Cat类的test方法。

  • 如果forwardingTargetForSelector返回的是nil,则会进入到methodSignatureForSelector方法,如果methodSignatureForSelector返回了一个合理的签名则会调用另外一个方法forwardInvocation,如果方法签名为空则不调用,NSInvocation封装了一个方法调用,包括:方法调用者、方法、方法参数
    • invocation.selector 方法名
    • invocation.target 方法调用者
    • [invocation getArgument:NULL atIndex:0]
//如果是类方法则需要将下面的方法变为+号
//methodSignatureForSelector也需要改为+号
- (id)forwardingTargetForSelector:(SEL)aSelector
{
    if (aSelector == @selector(test))
    {
        return nil;
        //return [[Cat alloc]init];
    }
    return [super forwardingTargetForSelector:aSelector];
}

//方法签名:返回值类型、参数类型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    if (aSelector == @selector(test))
    {
        return [NSMethodSignature signatureWithObjCTypes:"v16@0:8"];
    }
    return [super methodSignatureForSelector:aSelector];
}

// NSInvocation封装了一个方法调用,包括:方法调用者、方法、方法参数,其中Invocation.target为方法的调用者,Invocation.selector为方法名,Invocation.getArgument为获取方法参数
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
//    anInvocation.target = [[Cat alloc]init];
//    [anInvocation invoke];
    //或
    [anInvocation invokeWithTarget:[[Cat alloc]init] ];
}

完整的动态解析和消息转发流程如下图所示:

动态解析和消息转发.png
dynamic用法
@dynamic age;

上面的这句代码是提醒编译器不要自动生成 getset 方法,也不要自动生成成员变量


面试题
  • 讲一下OC的消息机制

    • OC中的方法调用其实都是转成了objc_msgSend函数的调用,给receiver(方法调用者)发送了一条消息(selector方法名)
  • objc_msgSend底层有3大阶段

    • 消息发送(当前类、父类中查找)、动态方法解析、消息转发

相关文章

网友评论

      本文标题:底层原理:Runtime之Objc_msgSend用法

      本文链接:https://www.haomeiwen.com/subject/cfzmyrtx.html