Runtime-objc_msgSend

01objc_msgSend

OC中的方法调用,就是给方法的调用着发送消息,其实都是转换为objc_msgSend函数的调用

objc_msgSend的执行流程可以分为3大阶段消息发送:给消息的接受者receiver,发送后面的那个消息,会在这个接管尝试找到这个方法然后调用,然后找到就调用成功。如果成功就不会进入后面的两步了

动态方法解析:如果消息发动阶段找不到对应的方法,就会进入这个阶段,允许开发者动态的创建一个方法,如果这个阶段,没有任何的操作,他就会进入下面的阶段消息转发:这里有可能会转发给另一个对象来调用这个方法 。
如果这三步都找不到方法的话,objc_msgSend就会报方法找不到的错误unrecognized selector sent to instance

GoodStudent *goodStudent = [[GoodStudent alloc] init];
        [goodStudent goodStudentTest];
这里有两个概念:
消息接收者:receiver
消息名称:goodStudentTest
        [GoodStudent goodStudentName];
//我们前面执行两个方法在内存中转成的代码,
 objc_msgSend)((id)goodStudent, sel_registerName("goodStudentTest"));
        objc_msgSend)((id)objc_getClass("GoodStudent"), sel_registerName("goodStudentName")

第一个阶段-消息发送01

打开runtime的源码
根据治疗我们可以,参考他的执行流程

在源码的objc-msg-arm64.s里面汇编代码实现

objc_msgSend流程

二分查找
// 2 3 4 5 8 10 11 120 找8
先去中间的,假如他比我们要找的小就在往后找,拿后面的所有的中间带来对比,一直到我们找到这个数为止。

第一个阶段-消息发送02

消息发动流程总结

如果是从class_rw_t中查找方法
已经排序的,二分查找
没有排序的,遍历查找
receiver通过isa指针找到receiverClass
receiverClass通过superclass指针找到superClass

04第二个阶段-动态方法解析01

进入动态方法解析之后基本的流程

动态方法解析流程
  • 开发者可以实现以下方法,来动态添加方法实现
+resolveInstanceMethod:
+resolveClassMethod:

动态解析过后,会重新走“消息发送”的流程
“从receiverClass的cache中查找方法”这一步开始执行
动态解析过后,会重新走“消息发送”的流程
“从receiverClass的cache中查找方法”这一步开始执行

一旦我们调用了没有实现的方法,他就是执行

+resolveInsatanceMethod:
+resolveClassMethod:
struct method_t{
     SEL sel;
     char *types;
     IMP imp;
};

-(void)otherTest
{
    NSLog(@"other test");
}
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    //这里面我要做操作
    if (sel == @selector(test))
    {
        //获取一个方法
       struct method_t *other = (struct method_t *)class_getInstanceMethod(self, @selector(otherTest));
        NSLog(@"test meiyoushixian");
        class_addMethod(self, sel, other->imp, other->types);
    }
   return  [super resolveInstanceMethod:sel];
}

05-动态方法解析02

使用另一种方式添加方法

Method method = class_getInstanceMethod(self, @selector(test));
        //不强转来实现
class_addMethod(self, sel, method_getImplementation(method), method_getTypeEncoding(method));

//或者我们自己写c函数来调用

void c_test(id self,SEL _imp)
{
    NSLog(@"C函数的实现");
}
class_addMethod(self, sel, (IMP)c_test, "v16@0:8");

类方法的动态添加和对象方法相似,只不过是给元类的对象添加,所以注意添加的对象

class_addMethod(object_getClass(self), sel, (IMP)c_test, "v16@0:8");

06动态方法解析03

什么情况下会用到,平时很少用到,基本都是为了面试。
动态添加的方法也是在方法列表里面

07第三阶段-消息转发01

在前面的两步都找不到方法的实现,他就会进入消息转发阶段

08第三阶段-消息转发02

一旦进入消息转发阶段,他就就会尝试调用

forwardingTargetForSelector:

两种- +都有可能

- (id)forwardingTargetForSelector:(SEL)aSelector
{
    if (aSelector == @selector(eat))
    {
        return [[Dog alloc] init];
    }
    return [super forwardingTargetForSelector:aSelector];
}

进入消息转发之后的源码是不开源码的,要用到逆行的知识才可以了解,大概的流程
1.先通过执行forwardingTargetForSelector, 如果返回值不为空,拿到返回值,给这个对象发消息objc_msgSend(cat, @selector(eat))

第三阶段-消息转发03.mp4

  1. 假如forwardingTargetForSelector没有实现,相当于返回值为空,他会调用另一个方法receiver methodSignatureForSelector:sel。也方法签名的意思也就是告诉接受者,你方法的返回值,类型是什么.
    3 如果你返回的方法签名是有值的,他会将这个签名封装成一个Invocation,(包括了方法接受者,方法,参数)然后就会调用另一个方法,将invocation传入
    forwardInvocation: 就可以在这个方法里处理你的操作开发者方法中自定义任何逻辑
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    if (aSelector == @selector(eat))
    {
        return [NSMethodSignature signatureWithObjCTypes:"v16@0:8"];
    }
    return [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
//    anInvocation.target 方法接受者
//    anInvocation.selector 方法
//    [anInvocation getArgument:NULL atIndex:0];//获取参数
    Dog *dog = [Dog new];
    anInvocation.target = dog;
    [anInvocation invoke];
//    [anInvocation invokeWithTarget:dog];
 }
消息转发

通过doesNotRecognizedSelector抛出我们常见的那个错误

10第三阶段-消息转发04

如果没有实现forwardingTargetForSelector,他也是会来到这个methodSignatureForSelector:sel->)forwardInvocation:可以在这个方法里面做任务,这里面做的事情就是我们在外面,调用方法所执行的操作
可以修改参数,修改返回值

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    if (aSelector == @selector(eat:))
    {
        return [NSMethodSignature signatureWithObjCTypes:"v16@0:8i16"];
    }
    return [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    int age ;
    //reciver selector other arguments
    [anInvocation getArgument:&age atIndex:2];
    NSLog(@"%i", age + 10); //25
    
}

10第三阶段-消息转发05
方法签名,没有方法签名就不会玩下走了,会报错,方法签名决定方法的参数和返回值
类方法的方法转发,
元类对象就是一种特殊的类对象,类方法的解析和转发类似于对象想方法,只不过,不类对象,换成了元类对象。不管是对象方法火类方法,转发只关心,接受者和方法名

@interface Dog : NSObject
- (void)eat;
- (void)sing;
@end

+ (id)forwardingTargetForSelector:(SEL)aSelector
{
    return [[Dog alloc] init];
}

总结

介绍一下OC的消息机制
OC中的方法调用,都是转成了objc_msgSend函数的调用,其实就是给recover发送了一个消息,
objc_msgSend底层分了三大阶段
1.消息发送(当前类,父类中查找)
2.动态方法解析resolveInstanceMethod
3.消息转发

什么是runtime?平时的开发中有使用过么

@dynamic
我们平时下一个属性,他会自定帮我们生成_age, set,get的声明和实现
@dynamic age,就意味着我们不希望帮我们自动生成实现set,get不要自动生成成员变量;

消息转发可以在平时开发中用于消除,方法找不到的错误,为了防止程序的闪退率太高,我们可以自己处理一下没有实现方法的情况

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值